1. 概述
本文我们来分享,从 pipeline 中移除 ChannelHandler 的代码具体实现。
在 《精尽 Netty 源码解析 —— ChannelPipeline(一)之初始化》 中,我们看到 ChannelPipeline 定义了一大堆移除 ChannelHandler 的接口方法:
ChannelPipeline remove(ChannelHandler handler); |
- 本文仅分享
#remove(ChannelHandler handler)
方法,从 pipeline 移除指定的 ChannelHandler 对象。
2. remove
#remove(ChannelHandler handler)
方法,从 pipeline 移除指定的 ChannelHandler 对象。代码如下:
|
调用
#getContextOrDie(ChannelHandler handler)
方法,获得对应的 AbstractChannelHandlerContext 节点。代码如下:private AbstractChannelHandlerContext getContextOrDie(ChannelHandler handler) {
AbstractChannelHandlerContext ctx = (AbstractChannelHandlerContext) context(handler);
if (ctx == null) { // die
throw new NoSuchElementException(handler.getClass().getName());
} else {
return ctx;
}
}
public final ChannelHandlerContext context(ChannelHandler handler) {
if (handler == null) {
throw new NullPointerException("handler");
}
AbstractChannelHandlerContext ctx = head.next;
// 循环,获得指定 ChannelHandler 对象的节点
for (;;) {
if (ctx == null) {
return null;
}
if (ctx.handler() == handler) { // ChannelHandler 相等
return ctx;
}
ctx = ctx.next;
}
}- 方法使用 Die 的原因是,获得不到节点的情况下,抛出 NoSuchElementException 异常。
- 调用
#remove(AbstractChannelHandlerContext ctx)
方法,移除指定 AbstractChannelHandlerContext 节点。
#remove(AbstractChannelHandlerContext ctx)
方法,移除指定 AbstractChannelHandlerContext 节点。代码如下:
代码的整体结构,和
#addLast(EventExecutorGroup group, String name, ChannelHandler handler)
方法是一致的。
1: private AbstractChannelHandlerContext remove(final AbstractChannelHandlerContext ctx) { |
- 第 4 行:
synchronized
同步,为了防止多线程并发操作 pipeline 底层的双向链表。 - 第 6 行:调用
#remove0(AbstractChannelHandlerContext ctx)
方法,从 pipeline 移除指定的 AbstractChannelHandlerContext 节点。详细解析,见 「3. remove0」 。 - ========== 后续分成 3 种情况 ==========
<1>
- 第 12 行:Channel 并未注册。
- 第 13 行:调用
#callHandlerCallbackLater(AbstractChannelHandlerContext, added)
方法,添加 PendingHandlerRemovedTask 回调。在 Channel 注册完成后,执行该回调。详细解析,见 「8. PendingHandlerCallback」 。 <2>
- 第 19 行:不在 EventLoop 的线程中。
- 第 20 至 26 行:提交 EventLoop 中,调用
#callHandlerRemoved0(AbstractChannelHandlerContext)
方法,执行回调 ChannelHandler 移除完成( removed )事件。详细解析,见 「4. callHandlerRemoved0」 。 <3>
- 这种情况,是
<2>
在 EventLoop 的线程中的版本。也因为此,已经确认在 EventLoop 的线程中,所以不需要在synchronized
中。 - 第 32 行:和【第 24 行】的代码一样,调用
#callHandlerRemoved0(AbstractChannelHandlerContext)
方法,执行回调 ChannelHandler 移除完成( removed )事件。
3. remove0
#remove0(AbstractChannelHandlerContext ctx)
方法,从 pipeline 移除指定的 AbstractChannelHandlerContext 节点。代码如下:
private static void remove0(AbstractChannelHandlerContext ctx) { |
FROM 闪电侠 《netty 源码分析之 pipeline(一)》
结合这两幅图,可以很清晰地了解移除 Handler 的过程,另外,被删除的节点因为没有对象引用到,果过段时间就会被 gc 自动回收。
4. callHandlerRemoved0
#callHandlerRemoved0(AbstractChannelHandlerContext)
方法,执行回调 ChannelHandler 移除完成( removed )事件。代码如下:
1: private void callHandlerRemoved0(final AbstractChannelHandlerContext ctx) { |
- 第 6 行:调用
ChannelHandler#handlerRemoved(AbstractChannelHandlerContext)
方法,回调 ChannelHandler 移除完成( removed )事件。一般来说,通过这个方法,来释放 ChannelHandler 占用的资源。注意,因为这个方法的执行在 EventLoop 的线程中,所以要尽量避免执行时间过长。 - 第 9 行:调用
AbstractChannelHandlerContext#setRemoved()
方法,设置 AbstractChannelHandlerContext 已移除。 - 第 11 至 15 行:发生异常,触发异常的传播。详细解析,见 《精尽 Netty 源码解析 —— ChannelPipeline(六)之异常事件的传播》 。
5. PendingHandlerRemovedTask
PendingHandlerRemovedTask 实现 PendingHandlerCallback 抽象类,用于回调移除 ChannelHandler 节点。代码如下:
private final class PendingHandlerRemovedTask extends PendingHandlerCallback { |
- 在
#execute()
实现方法中,我们可以看到,和#remove((AbstractChannelHandlerContext ctx)
方法的【第 17 至 32 行】的代码比较类似,目的是,在 EventLoop 的线程中,执行#callHandlerRemoved0(AbstractChannelHandlerContext)
方法,回调 ChannelHandler 移除完成( removed )事件。 <1>
处,为什么 PendingHandlerRemovedTask 可以直接提交到 EventLoop 中呢?因为 PendingHandlerRemovedTask 是个 Runnable ,这也就是为什么 PendingHandlerCallback 实现 Runnable 接口的原因。
666. 彩蛋
水文一小篇。推荐阅读文章: