1. Channel,EventLoop and ChannelFuture
This section will discuss the Channel, EventLoop, and ChannelFuture classes, which together can be considered representative of the Netty network abstraction.
- Channel: Socket
- EventLoop: control flow, multi-threaded processing, concurrency
- CHannelFuture: Asynchronous notifications
1.1 Channel Interface
Netty’s Channel interface corresponds to Java network programming’s Socket, greatly reducing the complexity of using the Socket class directly. In addition, Channel also has other predefined implementation classes.
- EmbeddedChannel: Test ChannelHandler
- LocalServerChannel: Used to implement communication between client and server within the same JVM
- NioSocketChannel: Asynchronous client-side TCP socket connection
- NioServerSocketChannel: Asynchronous server-side TCP socket connection
- NioDatagramChannel: Asynchronous UDP connection
- NioSctpChannel: Asynchronous client-side Sctp connection
- NioSctpServerChannel: Asynchronous Sctp server-side connection
- OioSocketChannel: synchronous client-side TCP socket connection
- OioServerSocketChannel: Synchronous server-side TCP socket connection
- OioDatagramChannel: Synchronous UDP connection
- OioSctpChannel: Synchronized Sctp server-side connection
- OioSctpServerChannel: Synchronized client-side TCP socket connection
1.2 EventLoop Interface
EventLoop is used to handle events that occur during the life of a connection. The following diagram illustrates the relationship between Channel, EventLoop, Thread, and EventLoopGroup.
These relationships are.
- An EventLoopGroup contains one or more EventLoops
- An EventLoop is bound to only one Thread for the life of the EventLoop
- All IO events handled by the EventLoop will be handled on its own Thread
- A Channel only registers one EventLoop for its lifetime
- An EventLoop may be assigned to one or more Channels
1.3 ChannelFuture Interface
All of Netty’s IO operations are asynchronous, and an operation may not return a result immediately, so we need a way to determine its result at some point later. ChannelFutureListener to be notified when an operation completes (whether successful or not)
2. ChannelHandler and ChannelPipeline
2.1 ChannelHandler Interface
ChannelHandler can be thought of as a container for application logic responsible for handling inbound and outbound data, such as converting data from one format to another, handling thrown exceptions, etc. ChannelInboundHandler is a frequently used sub-interface, and this type of ChannelHandler receives inbound events and data that will then be processed by your business logic locks. You can also flush data from the ChannelInboundHandler when you want to send a response to the client, and typically the application’s business logic resides in one or more ChannelInboundHandlers.
2.2 ChannelPipeline Interface
The ChannelPipeline provides the container for the ChannelHandler chain and defines the API for propagating inbound and outbound event streams on the chain. when a Channel is created, it is automatically assigned to its own ChannelPipeline.
The process of installing a ChannelHandler into a ChannelPipeline is shown below.
- A ChannelInitializer implementation is registered with ServerBootstrap
- When the ChannelInitializer.initChannel() method is called, the ChannelInitializer installs a custom set of ChannelHandlers in the ChannelPipeline
- ChannelInitializer removes itself from the ChannelPipeline
ChannelHandlers can be thought of as generic containers for any code that handles events (including data) to and from a ChannelPipeline, and it is the job of the ChannelHandler to make the event flow through the ChannelPipeline, which is installed during the initialization or boot phase of the application. These ChannelHandlers receive events, execute the implemented business logic, and pass data to the next ChannelHandler in the chain. the order in which they are executed is determined by the order in which they are added. In effect, the ChannelPipeline is the order in which these ChannelHandlers are organized.
When a ChannelHandler is added to a ChannelPipeline, it is assigned a ChannelHandlerContext, which represents the binding between the ChannelHandler and the ChannelPipeline, and while this object can be used to fetch the underlying Channel, it is primarily used to write outbound data.
There are two ways to send messages in Netty, either directly to the Channel or to the ChannelHandlerContext object associated with the ChannelHandler. The former will cause the message to flow from the end of the ChannelPipeline, while the latter will cause the message to flow from the next ChannelHandler in the ChannelPipeline.
3. Encoders and decoders
When you send or receive a message through Netty, a data conversion occurs. An inbound message is decoded, that is, converted from bytes to another format, usually a Java object. In the case of an outbound message, a conversion occurs in the opposite direction, from the current format being encoded into bytes. For this purpose, Netty provides different types of abstract classes for encoders and decoders, and the names of these base classes will be similar to ByteToMessageDecoder or MessageToByteEncoder. for some special types, there may also be ProtobufEncoder and For some special types, there may also be names like ProtobufEncoder and ProtobufDecoder, which are used to support Google’s Protocol Buffers.
Using the encoder/decoder provided by Netty, you will see that the channelRead method/event has been overridden for inbound data. For each message read from the inbound channel, the rewritten channelRead method will be called. It will then call the decode() method provided by the decoder to forward the decoded bytes to the next ChannelInboundHandler in the ChannelPipeline. outbound messages are the other way around, the encoder converts the messages to bytes and forwards them to the next ChannelOutboundHandler.
Netty’s bootstrapping classes provide containers for the network layer configuration of an application, which involves binding a process to a specified port or connecting a process to another process running on a specified port on a specified host. Usually we refer to the previous use case as bootstrapping a server and the later use case as bootstrapping a client. Thus, there are two types of bootstrapping: one for clients (Bootstrap) and another (ServerBootstrap) for servers.
The difference between the two types of bootstrap classes is as follows.
ServerBootstrap will bind to a port because the server has to listen for connections, while Bootstrap is used by the client application that wants to connect to the remote node.
Only one EventLoopGroup is needed to bootstrap a client, but two are needed for a ServerBootstrap. Because the server requires two different groups of Channels, the first group will contain only one ServerChannel, representing the server’s own listening sockets that are bound to a local port, while the second group will contain all the Channels that have been created to handle incoming client connections.
The EventLoopGroup associated with the ServerChannel will be assigned an EventLoop responsible for creating a Channel for incoming connection requests, and once the connection is accepted, the second EventLoopGroup will assign an EventLoop to its Channel.