Offer to come, dig friends take it! I am participating in the 2022 Spring Recruit Punch card activity. Click here for details

Netty version: NetTY-4.1.75.final

1. Introduction

ChannelHandler has two inherited interfaces, ChannelInboundHandler and ChannelOutboundHandler. ChannelHandler has two inherited interfaces, ChannelInboundHandler and ChannelOutboundHandler. ChannelHandler () :

Tips: The following two items will be updated in a future post

2. ChannelHandler execution sequence

ChannelHandler Execution sequence Specifies the execution sequence of the ChannelHandler, ChannelInboundHandler, and ChannelOutboundHandler classes. Print the order of execution using a simple Netty example. More code here will not directly paste out, has been uploaded to the Github repository, you can download to run locally.

Github address: github.com/mxsm/spring…

The results are divided into two parts:

  • Server Result

  • Client results

Next, the results are analyzed in combination with the source code. The execution of the client-side and server-side ChannelHandler is mostly the same, with minor differences. We’ll explain where there are differences

The server thread model is master-slave, so we will analyze the ChannelHandler in the Boss thread and the ChannelHandler in the Work thread separately.

General process:

The following analysis if not specified are based on the server as an example of source code analysis.

2.1 ChannelHandler# handlerAdded

Tips: Code address github.com/mxsm/spring…

After ServerBootstrap is created, a NioServerSocketChannel instance will be created, and then the ServerBootstrap#init method will be called to initialize it, as shown in the following figure:

As shown in the code above, I’ve circled it into three parts:

Tips: ChannelInitializer is actually a ChannelInboundHandlerAdapter

  1. ChannelInitializer#initChannel = ChannelInitializer#initChannel

The ChannelInitializer#initChannel private method is called after the channelRegistered method is triggered, which in turn calls the ChannelInitializer#initChannel abstract method.

  1. NioServerSocketChannel instance’s ChannelPipeline adds the ChannelHandler set by the ServerBootstrap#handler method. The example above is the ChannelHandler in this code, as shown below at position 1:

  1. This is also where data processing is handed over to the Worker thread. (There will be a follow-up article on how the main and negative threads work together.)

Conclusion: It can be seen from the above analysis that the ChannelHandler#handlerAdded method is triggered mainly through the add type method of ChannelPipeline. The underlying is through AbstractChannelHandlerContext# callHandlerAdded calls to achieve.

2.2 ChannelInboundHandler# channelRegistered

In the call to the ServerBootstrap#bind method, after the ServerSocketChannel is initialized, register the ServerSocketChannel with the BossGroup as shown in the following figure:

The location shown in figure 1 is to register ServerSocketChannel with the BossGroup. The follow-up code ultimately callsAbstractChannel#register0Method, as shown in the figure below:

Position 2, as shown above, triggers the ChannelInboundHandler#channelRegistered method.

Summary: the ChannelInboundHandler#channelRegistered method fires when a Channel is added to the EventLoopGroup

ChannelHandler is triggered in the NioServerSocketChannel instance, also known as the BossGroup. Where does the ChannelHandler trigger in workGroup trigger? Channelhandler’s channelRegistered method is triggered by the code in the position marked 3 in the previous section of the ChannelHandler#handlerAdded:

2.3 ChannelOutboundHandler# bind

Once the NioServerSocketChannel is created, initialized, and registered with the EventLoopGroup, it is bound, AbstractBootstrap#doBind0 method is called, as shown in the following figure:

Tips: the channel variable here is actually an instance of NioServerSocketChannel.

Through the following code can be found that the final call the bind method is AbstractChannelHandlerContext# invokeBind

The ChannelOutboundHandler#bind call is triggered by a server Channel bound to a local address, such as NioServerSocketChannel bound to a local address port ready to accept client data **

ChannelOutboundHandler#bind is unique to BossGroup channels and is not performed in childHandler.

2.4 ChannelInboundHandler# channelActive

ChannelInboundHandler#channelActive triggers in two cases:

  1. Description The ServerSocketChannel in the BossGroup was triggered
  2. Description A SocketChannel in a WorkerGroup was triggered

First look at the ServerSocketChannel trigger in BossGroup. When NioServerSocketChannel#bind is executed, the custom TimeServerBossOutHandler#bind is triggered:

Above grade 1 call the parent class of the bind method again, finally call AbstractChannel. AbstractUnsafe# bind:

The position 1 above triggers ChannelInboundHandler#channelActive.

TimeServerBossOutHandler# remove super.bind(CTX, localAddress, promise); This code, you use the client link to find not connected. This is because NioServerSocketChannel is not bound.

How do SocketChannel triggers in a WorkerGroup? ServerBootstrap#init = ServerBootstrap#init = ServerBootstrap#init = ServerBootstrap#init

ServerBootstrapAcceptor is also a ChannelInboundHandler:

The box above shows how to register a Channel into a WorkGroup. So this will trigger ChannelInboundHandler#channelRegistered.

There is no difference between BossGroup registering NioServerSocketChannel and WorkGroup registering SocketChannel triggering ChannelInboundHandler#channelRegistered.

Registered in the end is also called the AbstractChannel. AbstractUnsafe# register0:

The difference between BossGroup registering NioServerSocketChannel and WorkGroup registering NioSocketChannel is the isActive() method labeled 1 above, which is an abstract method. Based on different similar implementations.

  • NioServerSocketChannel implementation isActive ()

  • NioSocketChannel implementation isActive ()

So it goes into the if condition, plus it’s the first registration, and it eventually fires the method labeled 2.

NioServerSocketChannel and NioSocketChannel trigger ChannelInboundHandler#channelActive differently, but both trigger when a Channel is available

2.5 ChannelOutboundHandler# read

Main trigger channelActive AbstractChannelHandlerContext# invokeChannelActive method below:

Then by calling AbstractChannelHandlerContext# invokeChannelActive method:

Through above can know the final call is DefaultChannelPipeline HeadContext# channelActive method

Then call DefaultChannelPipeline. HeadContext# readIfIsAutoRead

The AbstractChannel#read method is then called, where ChannelPipeline#read is called to trigger ChannelOutboundHandler#read.

Conclusion: ChannelOutboundHandler#read is triggered at ChannelInboundHandler#channelActive, Through DefaultChannelPipeline. HeadContext# readIfIsAutoRead method implementation.

2.6 ChannelInboundHandler# channelRead

Both ServerSocketChannel and SocketChannel are triggered by a code in the NioEventLoop#processSelectedKey method. Read () :

The Unsafe implementation is performed differently depending on whether ServerSocketChannel is a SocketChannel.

ServerSocketChannel namely BossGroup execution is AbstractNioMessageChannel NioMessageUnsafe# read method:

Label 1 triggers ChannelInboundHandler#channelRead, and label 2 triggers ChannelInboundHandler#channelReadComplete.

SocketChannel workGroup is performed is AbstractNioByteChannel NioByteUnsafe# read method:

The labels 1,2 above trigger ChannelInboundHandler#channelRead and ChannelInboundHandler#channelReadComplete, respectively.

There are two more in workGroup:

TimeServerOutHandler--write
TimeServerOutHandler--flush
Copy the code

That’s just why? This is because the TimeServerInHandler calls the following method:

So let’s do the analysis

2.7 ChannelOutboundHandler# write and ChannelOutboundHandler# flush

To trigger ChannelOutboundHandler#write and ChannelOutboundHandler#flush, you need to call ChannelHandlerContext#writeAndFlush Is AbstractChannelHandlerContext# write:

Label 1 finds ChannelOutboundHandler, and then execute 2, Execute ChannelOutboundHandler#write or ChannelOutboundHandler#write and ChannelOutboundHandler#flush.

2.8 ChannelOutboundHandler# channelInactive and ChannelOutboundHandler# channelUnregistered

When the client closes the server it calls the AbstractNioByteChannel#read method with the following code:

Will call label 2 position, and then call AbstractNioByteChannel. NioByteUnsafe# closeOnRead method:

Follow up code found finally call AbstractChannel. AbstractUnsafe# deregister method. In this method the following code is called:

This triggers ChannelOutboundHandler#channelInactive and ChannelOutboundHandler#channelUnregistered.

2.8 ChannelHandler# handlerRemoved

Above grade 2 calls the pipeline. FireChannelUnregistered (); Methods, finally is to call the DefaultChannelPipeline HeadContext# channelUnregistered method:

The position 1 above is the one that triggers ChannelHandler#handlerRemoved. Remove the current Channel’s ChannelHandler from the EventLoop.

2.9 ChannelOutboundHandler# connect and ChannelOutboundHandler# close

Both of these occur on the client side, the overall trigger mechanism and the above mentioned roughly the same, you can go to the analysis.

3. Summary

The overall trigger flow for Netty’s ChannelHandler is described above. There is no error-catching trigger involved.

  • ChannelPipeline two-way linked list HeadContext and TailContext ChannelHandler, inherited AbstractChannelHandlerContext at the same time, You could also say ChannelHandlerContext.
  • ChannelHander added to the queue is wrapped as ChannelHandlerContext

Tips: I am ant back elephant, the article is helpful to you like to pay attention to me, the article has incorrect places please be corrected to leave comments ~ thank you