
一文聊透Netty IO事件的编排利器pipeline |详解所有IO事件(七)
6. 从 pipeline 删除 channelHandler
从上个小节的内容中我们可以看到向 pipeline 中添加 ChannelHandler 的逻辑还是比较复杂的,涉及到的细节比较多。
那么在了解了向 pipeline 中添加 ChannelHandler 的过程之后,从 pipeline 中删除 ChannelHandler 的逻辑就变得很好理解了。
netty 提供了以上三种方式从 pipeline 中删除指定 ChannelHandler ,下面我们以第一种方式为例来介绍 ChannelHandler 的删除过程。
6.1 getContextOrDie
首先需要通过 getContextOrDie 方法在 pipeline 中查找到指定的 ChannelHandler 对应的 ChannelHandelrContext 。以便确认要删除的 ChannelHandler 确实是存在于 pipeline 中。
context 方法是通过遍历 pipeline 中的双向链表来查找要删除的 ChannelHandlerContext 。
6.2 remove
remove 方法的整体代码结构和 addLast0 方法的代码结构一样,整体逻辑也是先从 pipeline 中的双向链表结构中将指定的 ChanneHandlerContext 删除,然后在处理被删除的 ChannelHandler 中 handlerRemoved 方法的回调。
1.从 pipeline 中删除指定 ChannelHandler 对应的 ChannelHandlerContext 。逻辑比较简单,就是普通双向链表的删除操作。
2.如果此时 channel 并未向对应的 reactor 进行注册,则需要向 pipeline 的任务列表中添加 PendingHandlerRemovedTask 任务,再该任务中会执行 ChannelHandler 的 handlerRemoved 回调,当 channel 向 reactor 注册成功后,reactor 会执行 pipeline 中任务列表中的任务,从而回调被删除 ChannelHandler 的 handlerRemoved 方法。
pipeline任务.png
在执行 ChannelHandler 中 handlerRemoved 回调的时候,需要对 ChannelHandler 的状态进行判断:只有当 handlerState 为 ADD_COMPLETE 的时候才能回调 handlerRemoved 方法。
这里表达的语义是只有当 ChannelHanler 的 handlerAdded 方法被回调之后,那么在 ChannelHanler 被从 pipeline 中删除的时候它的 handlerRemoved 方法才可以被回调。
在 ChannelHandler 的 handlerRemove 方法被回调之后,将 ChannelHandler 的状态设置为 REMOVE_COMPLETE 。
3.如果 channel 已经在 reactor 中注册成功,那么当 channelHandler 从 pipeline 中删除之后,需要立即回调其 handlerRemoved 方法。但是需要确保 handlerRemoved 方法在 channelHandler 指定的 executor 中进行。
7. pipeline 的初始化
其实关于 pipeline 初始化的相关内容我们在《详细图解 Netty Reactor 启动全流程》中已经简要介绍了 NioServerSocketChannel 中的 pipeline 的初始化时机以及过程。
在《Netty 如何高效接收网络连接》中笔者也简要介绍了 NioSocketChannel 中 pipeline 的初始化时机以及过程。
本小节笔者将结合这两种类型的 Channel 来完整全面的介绍 pipeline 的整个初始化过程。
7.1 NioServerSocketChannel 中 pipeline 的初始化
从前边提到的这两篇文章以及本文前边的相关内容我们知道,Netty 提供了一个特殊的 ChannelInboundHandler 叫做 ChannelInitializer ,用户可以利用这个特殊的 ChannelHandler 对 Channel 中的 pipeline 进行自定义的初始化逻辑。
如果用户只希望在 pipeline 中添加一个固定的 ChannelHandler 可以通过如下代码直接添加。
如果希望添加多个 ChannelHandler ,则可以通过 ChannelInitializer 来自定义添加逻辑。
由于使用 ChannelInitializer 初始化 NioServerSocketChannel 中 pipeline 的逻辑会稍微复杂一点,下面我们均以这个复杂的案例来讲述 pipeline 的初始化过程。
以上这些由用户自定义的用于初始化 pipeline 的 ChannelInitializer ,被保存至 ServerBootstrap 启动类中的 handler 字段中。用于后续的初始化调用
在服务端启动的时候,会伴随着 NioServeSocketChannel 的创建以及初始化,在初始化 NioServerSokcetChannel 的时候会将一个新的 ChannelInitializer 添加进 pipeline 中,在新的 ChannelInitializer 中才会将用户自定义的 ChannelInitializer 添加进 pipeline 中,随后才执行初始化过程。
Netty 这里之所以引入一个新的 ChannelInitializer 来初始化 NioServerSocketChannel 中的 pipeline 的原因是需要兼容前边介绍的这两种初始化 pipeline 的方式。
•一种是直接使用一个具体的 ChannelHandler 来初始化 pipeline。
•另一种是使用 ChannelInitializer 来自定义初始化 pipeline 逻辑。
忘记 netty 启动过程的同学可以在回看下笔者的《详细图解 Netty Reactor 启动全流程》这篇文章。
注意此时 NioServerSocketChannel 并未开始向 Main Reactor 注册,根据本文第四小节《4. 向 pipeline 添加 channelHandler 》中的介绍,此时向 pipeline 中添加这个新的 ChannelInitializer 之后,netty 会向 pipeline 的任务列表中添加 PendingHandlerAddedTask 。当 NioServerSocketChannel 向 Main Reactor 注册成功之后,紧接着 Main Reactor 线程会调用这个 PendingHandlerAddedTask ,在任务中会执行这个新的 ChannelInitializer 的 handlerAdded 回调。在这个回调方法中会执行上边 initChannel 方法里的代码。
server channel pipeline 注册前结构.png
当 NioServerSocketChannel 在向 Main Reactor 注册成功之后,就挨个执行 pipeline 中的任务列表中的任务。
执行 pipeline 任务列表中的 PendingHandlerAddedTask 任务:
最终在 PendingHandlerAddedTask 中执行 pipeline 中 ChannelInitializer 的 handlerAdded 回调。
这个 ChannelInitializer 就是在初始化 NioServerSocketChannel 的 init 方法中向 pipeline 添加的 ChannelInitializer。
在 handelrAdded 回调中执行 ChannelInitializer 匿名类中 initChannel 方法,注意此时执行的 ChannelInitializer 类为在本小节开头 init 方法中由 Netty 框架添加的 ChannelInitializer ,并不是用户自定义的 ChannelInitializer 。
执行完 ChannelInitializer 匿名类中 initChannel 方法后,需将 ChannelInitializer 从 pipeline 中删除。并回调 ChannelInitializer 的 handlerRemoved 方法。删除过程笔者已经在第六小节《6. 从 pipeline 删除 channelHandler》详细介绍过了。
当执行完 initChannel 方法后此时 pipeline 的结构如下图所示:
serverSocketChannel注册之后pipeline结构.png
当用户的自定义 ChannelInitializer 被添加进 pipeline 之后,根据第四小节所讲的添加逻辑,此时 NioServerSocketChannel 已经向 main reactor 成功注册完毕,不再需要向 pipeine 的任务列表中添加 PendingHandlerAddedTask 任务,而是直接调用自定义 ChannelInitializer 中的 handlerAdded 回调,和上面的逻辑一样。不同的是这里最终回调至用户自定义的初始化逻辑实现 initChannel 方法中。执行完用户自定义的初始化逻辑之后,从 pipeline 删除用户自定义的 ChannelInitializer 。
随后 netty 会以异步任务的形式向 pipeline 的末尾添加 ServerBootstrapAcceptor ,至此 NioServerSocketChannel 中 pipeline 的初始化工作就全部完成了。
7.2 NioSocketChannel 中 pipeline 的初始化
在 7.1 小节中笔者举的这个 pipeline 初始化的例子相对来说比较复杂,当我们把这个复杂例子的初始化逻辑搞清楚之后,NioSocketChannel 中 pipeline 的初始化过程就变的很简单了。
在《Netty 如何高效接收网络连接》一文中我们介绍过,当客户端发起连接,完成三次握手之后,NioServerSocketChannel 上的 OP_ACCEPT 事件活跃,随后会在 NioServerSocketChannel 的 pipeline 中触发 channelRead 事件。并最终在 ServerBootstrapAcceptor 中初始化客户端 NioSocketChannel 。
主从Reactor组完整结构.png
在这里会将用户自定义的 ChannelInitializer 添加进 NioSocketChannel 中的 pipeline 中,由于此时 NioSocketChannel 还没有向 sub reactor 开始注册。所以在向 pipeline 中添加 ChannelInitializer 的同时会伴随着 PendingHandlerAddedTask 被添加进 pipeline 的任务列表中。
niosocketchannel中pipeline的初始化.png
后面的流程大家应该很熟悉了,和我们在7.1小节中介绍的一模一样,当 NioSocketChannel 再向 sub reactor 注册成功之后,会执行 pipeline 中的任务列表中的 PendingHandlerAddedTask 任务,在 PendingHandlerAddedTask 任务中会回调用户自定义 ChannelInitializer 的 handelrAdded 方法,在该方法中执行 initChannel 方法,用户自定义的初始化逻辑就封装在这里面。在初始化完 pipeline 后,将 ChannelInitializer 从 pipeline 中删除,并回调其 handlerRemoved 方法。
至此客户端 NioSocketChannel 中 pipeline 初始化工作就全部完成了。
