Netty源码解析(八)回到Channel的register操作
本文最后更新于:April 11, 2022 pm
Netty 源码分析系列:
Netty 源码解析(一): 开始
Netty 源码解析(二): Netty 的 Channel
Netty 源码解析(三): Netty 的 Future 和 Promise
Netty 源码解析(四): Netty 的 ChannelPipeline
Netty 源码解析(五): Netty 的线程池分析
Netty 源码解析(六): Channel 的 register 操作
Netty 源码解析(七): NioEventLoop 工作流程
Netty 源码解析(八): 回到 Channel 的 register 操作
Netty 源码解析(九): connect 过程和 bind 过程分析
目录
回到 Channel 的 register 操作
我们回到前面的 register0(promise) 方法,我们知道,这个 register 任务进入到了 NioEventLoop 的 taskQueue 中,然后会启动 NioEventLoop 中的线程,该线程会轮询这个 taskQueue,然后执行这个 register 任务。
注意,此时执行该方法的是 eventLoop 中的线程:
// AbstractChannel
1 |
|
我们先说掉上面的 doRegister() 方法,然后再说 pipeline。
1 |
|
我们可以看到,这里做了 JDK 底层的 register 操作,将 SocketChannel(或 ServerSocketChannel) 注册到 Selector 中,并且可以看到,这里的监听集合设置为了 0,也就是什么都不监听。
当然,也就意味着,后续一定有某个地方会需要修改这个 selectionKey 的监听集合,不然啥都干不了
我们重点来说说 pipeline 操作,我们之前在介绍 NioSocketChannel 的 pipeline 的时候介绍到,我们的 pipeline 现在长这个样子:
现在,我们将看到这里会把 LoggingHandler 和 EchoClientHandler 添加到 pipeline。
我们继续看代码,register 成功以后,执行了以下操作:
1 |
|
大家可以跟踪一下,这一步会执行到 pipeline 中 ChannelInitializer 实例的 handlerAdded 方法,在这里会执行它的 init(context) 方法:
1 |
|
然后我们看下 initChannel(ctx),这里终于来了我们之前介绍过的 init(channel) 方法:
1 |
|
我们前面也说过,ChannelInitializer 的 init(channel) 被执行以后,那么其内部添加的 handlers 会进入到 pipeline 中,然后上面的 finally 块中将 ChannelInitializer 的实例从 pipeline 中删除,那么此时 pipeline 就算建立起来了,如下图:
其实这里还有个问题,如果我们在 ChannelInitializer 中添加的是一个 ChannelInitializer 实例呢?大家可以考虑下这个情况。
pipeline 建立了以后,然后我们继续往下走,会执行到这一句:
1 |
|
我们只要摸清楚了 fireChannelRegistered() 方法,以后碰到其他像 fireChannelActive()、fireXxx() 等就知道怎么回事了,它们都是类似的。我们来看看这句代码会发生什么:
// DefaultChannelPipeline
1 |
|
也就是说,我们往 pipeline 中扔了一个 channelRegistered 事件,这里的 register 属于 Inbound 事件,pipeline 接下来要做的就是执行 pipeline 中的 Inbound 类型的 handlers 中的 channelRegistered() 方法。
从上面的代码,我们可以看出,往 pipeline 中扔出 channelRegistered 事件以后,第一个处理的 handler 是 head。
接下来,我们还是跟着代码走,此时我们来到了 pipeline 的第一个节点 head 的处理中:
// AbstractChannelHandlerContext
1 |
|
也就是说,这里会先执行 head.invokeChannelRegistered() 方法,而且是放到 NioEventLoop 中的 taskQueue 中执行的:
// AbstractChannelHandlerContext
1 |
|
我们去看 head 的 channelRegistered 方法:
// HeadContext
1 |
|
然后 head 会执行 fireChannelRegister() 方法:
// AbstractChannelHandlerContext
1 |
|
注意:pipeline.fireChannelRegistered() 是将 channelRegistered 事件抛到 pipeline 中,pipeline 中的 handlers 准备处理该事件。而 context.fireChannelRegistered() 是一个 handler 处理完了以后,向后传播给下一个 handler。
它们两个的方法名字是一样的,但是来自于不同的类。
findContextInbound() 将找到下一个 Inbound 类型的 handler,然后又是重复上面的几个方法。
我觉得上面这块代码没必要太纠结,总之就是从 head 中开始,依次往下寻找所有 Inbound handler,执行其 channelRegistered(ctx) 操作。
说了这么多,我们的 register 操作算是真正完成了。
下面,我们回到 initAndRegister 这个方法:
1 |
|
我们要知道,不管是服务端的 NioServerSocketChannel 还是客户端的 NioSocketChannel,在 bind 或 connect 时,都会先进入 initAndRegister 这个方法,所以我们上面说的那些,对于两者都是通用的。
大家要记住,register 操作是非常重要的,要知道这一步大概做了哪些事情,register 操作以后,将进入到 bind 或 connect 操作中。
本文作者: 墨水记忆
本文链接: https://tothefor.com/DragonOne/3981544359.html
版权声明: 本博客所有文章除特别声明外,均采用 CC BY-SA 4.0 协议 ,转载请注明出处!