One, foreword

Specially with leadership application at night do not work overtime today, because recent things too much, can’t calm down and seriously study Netty, home of the Netty combat has been on the shelf for ash for a long time, today also took out serious study, can strive for in the future project skilled use of Netty, today is Christmas Eve, I wish you all peace and happiness.

Netty components and design

1. Netty represents network abstraction

  • Channel
  • EventLoop
  • ChannelFuture

The Channel interface

For network programming, the basic structure is Socket. Netty Channel is to highly encapsulate Socket, provide simple and convenient API for developers to use, and greatly reduce the threshold of use. According to the content of Netty Field, There are several channels that Netty has already defined that can be used directly, such as:

  • EmbeddedChannel
  • LocalServerChannel
  • NioDatagramChannel
  • NioSctpChannel
  • NioSocketChannel
  • FileChannel
  • DatagramChannel
  • ServerSocketChannel

EventLoop

The EventLoopGroup is used to handle events that occur during the lifetime of a connection, as in the previous article when writing about the Echo server:

   //1. BoosGroup is mainly responsible for request work, just like the boss is mainly responsible for receiving orders, the specific operation is done by employees
    EventLoopGroup bossGroup = new NioEventLoopGroup(1);
    //2. WorkGroup is mainly responsible for specific operations, just like employees, the boss is responsible for receiving orders, and the specific implementation is the responsibility of employees
    EventLoopGroup workGroup = new NioEventLoopGroup(10);
Copy the code

An EventLoopGroup is a combination of multiple Eventloops used to process events. A bossGroup is used to listen for connection events, and a workGroup is used to perform specific tasks after a user connects

Basic operation

    1. Create a Channel
    2. Register a Channel with an EventLoop
    3. Use EventLoop to process I/O requests throughout the lifecycle

Channel EventLoopGroup Indicates the relationship between EventLoop threads

  • An EventLoopGroup contains one or more Eventloops
  • An EventLoop is bound to only one Thread during its lifetime
  • Only one EventLoop is registered in a Channel’s lifetime
  • An EventLoop may be assigned to multiple channels

ChannelFuture

ChannelFuture is basically a placeholder for asynchronous notifications because, for various reasons, notifications cannot be returned immediately and need to be returned at a certain point

ChannelFuture addListener(GenericFutureListener<? extends Future<? super Void>> listener);
Copy the code

The ChannelFuture addListener method registers a listener to be notified when an operation completes, whether it succeeds or fails

2. ChannelHandler and ChannelPipeline

2.1 ChannelHandler interface

ChannelHandler acts as a container for all application logic for inbound and outbound data. It can be used for any type of action. I was trying netty to build a WebSocket server that can handle not only strings but also webSocket-specified data types, such as:

ChannelInboundHandler, a subclass of ChannelHandler, is responsible for inbound information that will be processed by the application’s business logic. ChannelInboudHandler can also flush messages to the client if needed

2.2 ChannelPipeline interface

Channelpipeline uses a chain to store ChannelHandler, the API used to propagate the flow of inbound and outbound events on that chain. Register ChannelHandler with ChannlPipeline with the following code: The ChannelPipeline is used by the ChannelHandler to orchestrate the order

serverBootstrap.group(bossGroup,workGroup)
                       .childHandler(new ChannelInitializer<SocketChannel>() {
                           @Override
                           protected void initChannel(SocketChannel ch) throws Exception {
                               // Inject a custom Handler
                                ch.pipeline().addLast(newNettyServerHandler()); }});Copy the code

After the first ChannelHandler completes execution, the next ChannelHandler is called

For each new Channel Channel, a new Channel pipeline is created and attached to the Channel

The following figure is from the source code, you can clearly see that the inbound and outbound ChannelHandler is registered in the same ChannelPipeline, the basic inbound flow:

  • An inbound message or event is read, which flows from the header of the ChannelPipeline to the first ChannelInboundHandler for processing
  • When the first Handler completes processing, it passes to the second Handler for processing
  • Finally, data is passed to the end of the ChannelPipeline to indicate that the inbound operation is complete
  • Outbound, in contrast, passes data from the end of the ChannelPipeline to the header
  I/O Request
 *                                            via {@link Channel} or
 *                                        {@linkChannelHandlerContext} * | * +---------------------------------------------------+---------------+ * | ChannelPipeline |  | * | \|/ | * | +---------------------+ +-----------+----------+ | * | | Inbound Handler N | | Outbound Handler1* | | | + -- -- -- -- -- -- -- -- -- - + -- -- -- -- -- -- -- -- -- -- + + -- -- -- -- -- -- -- -- -- -- - + -- -- -- -- -- -- -- -- -- -- + | | * / | \ | | * | | \ | / * | | + -- -- -- -- -- -- -- -- -- - + -- -- -- -- -- -- -- -- -- -- + +-----------+----------+ | * | | Inbound Handler N-1 |            | Outbound Handler  2  |    |
 *  |    +----------+----------+            +-----------+----------+    |
 *  |              /|\                                  .               |
 *  |               .                                   .               |
 *  | ChannelHandlerContext.fireIN_EVT() ChannelHandlerContext.OUT_EVT()|
 *  |        [ method call]                       [method call]         |
 *  |               .                                   .               |
 *  |               .                                  \|/              |
 *  |    +----------+----------+            +-----------+----------+    |
 *  |    | Inbound Handler  2  |            | Outbound Handler M-1* | | | + -- -- -- -- -- -- -- -- -- - + -- -- -- -- -- -- -- -- -- -- + + -- -- -- -- -- -- -- -- -- -- - + -- -- -- -- -- -- -- -- -- -- + | | * / | \ | | * | | \ | / * | | + -- -- -- -- -- -- -- -- -- - + -- -- -- -- -- -- -- -- -- -- + +-----------+----------+ | * | | Inbound Handler1| | Outbound Handler M | | * | +----------+----------+ +-----------+----------+ | * | /|\ | | * + -- -- -- -- -- -- -- -- -- -- -- -- -- -- - + -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- - + -- -- -- -- -- -- -- -- -- -- -- -- -- -- - | \ | + * / * +---------------+-----------------------------------+---------------+ * | | | | * | [ Socket.read() ] [ Socket.write() ]  | * | | * | Netty Internal I/O Threads (Transport Implementation)| * + -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- +Copy the code

ChannelInboundHandler and ChannelOutboundHandler are both registered in the same ChannelPipeline. How can Netty distinguish Inbound from Outbound?

I’m going to think about this in more detail in a future article, but netty will help us automatically distinguish between inbound and outbound handlers and ensure that data is only passed between two handlers of the same type

What happens when the ChannelHandler registers in the ChannelPipeline?

ChannelHandler registers with ChannelPipeline and assigns a ChannelHandlerContext to bind the ChannelHandler to the ChannelPipeline. The ChannelHandlerContext can be used to get a Channel, but is mostly used to write outbound information

Two message sending modes of Netty

    1. Writing a message directly to a Channel causes the message to flow from the end of the Channel pipeline, which can also be understood in this way. Writing a message to a Channel means that it needs to respond to the client, and the server needs to call the write method, so it flows from the end

    2. The message is written to the ChannelHandlerContext object associated with ChannelHandler, causing the message to flow from the next ChannelHandler in the ChannelPipeline.