Channel of Netty Core Concept (5)

1 Introduction

 The previous section talked about the first key startup class of Netty, some operations performed by the startup class, and the execution process of the fixed handler of the channel on the server side. It was mentioned that whether it is the connect or the bind method, the related methods of the channel are finally called. This section begins the description of channels. There are many concepts of channel setting, and they are all very important. First, let's put a class structure diagram of NIO's client Channel.

2. Main Concepts

2.1 channel

   A channel is a data channel that directly communicates with the operating system layer. It may be provided by java, or it may be a channel that extends C++ functions through native methods, but no matter which type, there is a basic definition. The following are the main interface methods defined in channel:

  id(): Get the ID of the channel

  eventloop(): Get the thread pool registered by the channel

  parent(): Get the parent channel of the channel, NIO has no parent channel, generally null

  config(): Get the configuration of the channel

  isOpen(): Whether the channel is open

  isRegistered(): Whether the channel is registered in the thread pool

  isActive(): Whether the channel is available

  metadata(): The metadata of the channel

  localAddress(): the local binding address port of the channel

  remoteAddress(): The address port of the peer connected to the channel

  closeFuture(): The future triggered when the channel is closed

  isWritable(): Whether the channel is currently writable, only the IO thread will handle the writable state

  bytesBeforeUnwritable(): How many bytes can the channel still write

  unsafe(): Obtain the unsafe operation object of the channel. For channel read and write, generally do not directly operate the channel, but transfer it to the unsafe object for processing. The channel itself usually only performs the operation of querying the status and obtaining the contents of the relevant fields.

  alloc(): Get the allocated buffer

  read(): perform read operation

  write(): perform write operation

 In the above method, we saw an unfamiliar unsafe object, which is also a relatively important concept. Understanding the role of the class in the entire structure is of great help to understand the framework. Unsafe is directly defined inside the Channel interface, which means that the interface is bound to Channel, and the function of this class is also explained in the above method. The channel itself does not directly do the corresponding work, but is handed over to the unsafe method to call. The following figure is the interface definition of unsafe:

  recvBufAllocHandle(): Get the handler that is processed after reading the channel data

  localAddress(): local address port

  remoteAddress(): remote address port

  register(): register the channel to the thread pool

  bind(): The server binds the local port

  connect(): the client connects to the remote port

  disconnect(): disconnect

  close(): close the channel

  closeForcibly(): force close

  deregister(): remove the thread pool registration

  beginRead(): prepare to read data

  write(): write data

  flush(): Force flush

  voidPromise(): special promise

  outboundBuffer(): Buffer operation class for obtaining output data

 Looking at the above series of interfaces, you can understand that the actual operation of the channel is the unsafe class, but is the unsafe class directly operated? For example, when binding a port, the channel.bind method is indeed called. In fact, there are other concepts involved here, which are operated in a circle. You should also understand from the name of unsafe that the class that operates on the channel is a thread-unsafe class, so generally through the structural design of Netty itself, thread isolation can be ensured before it can be used with confidence. Of course, if you don't define an IO method yourself, there is basically no problem with using the current Netty package. If you build your own wheels, you need to pay extra attention to this.

2.2 ChannelPipeline

 This pipeline has been mentioned one after another in the previous section. In this section, we will talk about the role of this class. Don't look at the interface definition, first focus on the location of the class in the entire structure. Open AbstractChannel, take a closer look at the abstract channel class of this class, and you will gain something. Most of the main operation methods of the channel are done through the pipeline, such as: bind, connect, close, deregister, flush, read, write, etc. Strange? It's not handled by unsafe as we said above. But this is not contradictory. Unsafe is the most basic processing of the bottom layer. We will have a series of business layers that need to be processed. For example, the parameter settings of the socket during bind are handled by the handler, so the channel will delegate the relevant operations to the pipeline for processing. The pipeline goes through a series of operations, finally calls unsafe related actions, and finally returns to the channel. The pipeline translation is a pipeline, which feels more like a pipeline operation here.

 Now let's look at the basic definition of pipeline and it won't feel abrupt.

 There are many methods of pipline, but they are divided into two categories: 1. Registering the handler; 2. Using the handler; the screenshot is the method of using the handler. There are many ways to register the handler, which will not be introduced here. There are not so many implementation classes for ChannelPipeline, the basis is DefaultChannelPipeline, so some methods of this class are directly parsed.

 1. First, what does the method of registering the handler do, take the addLast() method used in the example as an example:

    public final ChannelPipeline addLast(EventExecutorGroup group, String name, ChannelHandler handler) {
        final AbstractChannelHandlerContext newCtx;
        synchronized (this) {
            checkMultiplicity(handler);

            newCtx = newContext(group, filterName(name, handler), handler);

            addLast0(newCtx);

            // If the registered is false it means that the channel was not registered on an eventloop yet.
            // In this case we add the context to the pipeline and add a task that will call
            // ChannelHandler.handlerAdded(...) once the channel is registered.
            if (!registered) {
                newCtx.setAddPending();
                callHandlerCallbackLater(newCtx, true);
                return this;
            }

            EventExecutor executor = newCtx.executor();
            if (!executor.inEventLoop()) {
                newCtx.setAddPending();
                executor.execute(new Runnable() {
                    @Override
                    public void run() {
                        callHandlerAdded0(newCtx);
                    }
                });
                return this;
            }
        }
        callHandlerAdded0(newCtx);
        return this;
    }

 To understand this code, there is another content to pay attention to, tail and head, and handlerContext (this is a handler-related concept, which will not be introduced here). tail and head are the header and tail nodes of the handler held by the pipeline. When you see addLast, you should understand that the handlers are connected in a chain structure. As mentioned earlier, the handler is the responsibility chain mode, which is sequential. in order. This method is to put the handler at the end of the responsibility chain. There is a process in the middle, which wraps the handler with context, which is not the focus of this section, and will be introduced later in the handler chapter.

 2. Next is the key point of the pipeline, how does it operate the channel, or what is the process of operating the channel.

    public final ChannelFuture bind(SocketAddress localAddress) {
        return tail.bind(localAddress);
    }

 Basic channel operations are done through the default tail, these operations include bind, connect, close, deregister, flush, read, write. tail is a handlerContext, here will involve some content of handlerContext, briefly: when adding handler to pipeline before, context is generated, context constitutes a chain structure, it knows which handler before and after it is. Others will not go into detail. In the end, the handler of the tail continues to advance the handler of the previous out type, and finally finds the head. You will understand when you look at the HeadContext class. This class obtains the unsafe object of the channel, and all operations are performed by The object completes, so the whole link is connected. The channelpipeline and unsafe objects are generated by the channel, and all operations are handed over to the pipeline. The pipeline searches from the tail to the head, and finally the head obtains the unsafe method of the channel, and finally performs related operations.

 There is also a class of methods that are also introduced here, which involve the running process of the handlerContext responsibility chain, so I won't go into details.

    public final ChannelPipeline fireChannelActive() {
        AbstractChannelHandlerContext.invokeChannelActive(head);
        return this;
    }

 This is called from head

     public void channelActive(ChannelHandlerContext ctx) throws Exception {
            ctx.fireChannelActive();

            readIfIsAutoRead();
        }

 Then I called my own fireChannelActive method, looking back from the head, the in-type method of next promoted the operation of the entire responsibility chain. Therefore, the fireChannelActive() method of the pipeline is the starting method, which promotes the invocation of the chain of responsibility.

3. Postscript

  There are three important concepts of channel:

    1. Channel itself does not do anything, and passes events to ChannelPipeline.

    2. The ChannelPipeline itself does not do anything. It mainly controls the handler chain. The tail queries the head to hold the unsafe object, controls the connection, reading, and writing of the channel. It provides the direct trigger event chain method fireXXX from the head to the tail. .

    3. Unsafe is the final location for operating the channel.

  Finally, a relationship diagram is attached:

  

Guess you like

Origin http://43.154.161.224:23101/article/api/json?id=324998193&siteId=291194637