Pipeline 的生命周期
本文以 Netty 4.2 为背景,分析 ChannelPipeline 从创建、添加 Handler、响应 Channel 注册、传播事件,到移除 Handler 和销毁链表的完整过程。
Pipeline 依附于 Channel 存在。一个 Channel 在构造时就会创建一个 Pipeline,之后 Pipeline 中 Handler 的生命周期又会受到 Channel 注册、注销和关闭流程的推动。因此,本文不会重复展开 Channel 的底层注册和关闭逻辑,只关注它们与 Pipeline 发生连接的位置。Channel 本身的状态变化可以参考《Channel的生命周期》。
1. 先建立整体认识
1.1 Pipeline 没有独立于 Channel 的生命周期
一个 Channel 只持有一个 Pipeline:
text
Channel
-> ChannelPipeline
-> HeadContext
-> 业务 ChannelHandlerContext
-> TailContext
Pipeline 没有独立状态,也没有单独注册到 EventLoop 的操作。它的关键阶段由 Channel 推动:
text
Channel 构造
-> 创建 Pipeline
向 Pipeline 添加 Handler
-> 创建 Context 并插入链表
Channel 注册
-> 执行注册前积累的 handlerAdded()
-> 传播 channelRegistered()
Channel 激活和读写
-> 在 Pipeline 中传播入站事件和出站操作
Channel 关闭并注销
-> 传播 channelInactive()
-> 传播 channelUnregistered()
-> 删除业务 Context
-> 执行 handlerRemoved()
这里需要区分两个概念:
- Pipeline 对象的生命周期:从 Channel 构造开始,最终与 Channel 一起变为不可达对象并由 GC 回收。
- Pipeline 中 Context 的生命周期:从插入链表开始,到移出链表并完成
handlerRemoved()为止。
现在我们根据这两条主线来阅读
1.2 Channel、Pipeline、Context 和 Handler 的关系
四者的职责如下:
text
Channel
表示一条网络通道,并持有一个 Pipeline
ChannelPipeline
维护由 ChannelHandlerContext 组成的双向链表
ChannelHandlerContext
保存链表位置、所属 Pipeline、Handler、执行器和生命周期状态
ChannelHandler
实现具体的入站事件或出站操作处理逻辑(我们的逻辑一般写在这里)
Pipeline 链表中真正连接的是 ChannelHandlerContext,不是 Handler 本身。DefaultChannelHandlerContext 通过组合关系持有 Handler:
java
final class DefaultChannelHandlerContext extends AbstractChannelHandlerContext {
private final ChannelHandler handler;
DefaultChannelHandlerContext(
DefaultChannelPipeline pipeline,
EventExecutor executor,
String name,
ChannelHandler handler) {
super(pipeline, executor, name, handler.getClass());
this.handler = handler;
}
}
Handler 只负责处理逻辑。它在当前 Pipeline 中的位置、执行线程和生命周期状态属于 Context,。
2. Pipeline 的创建
2.1 AbstractChannel 创建 Pipeline
Pipeline 在 AbstractChannel 的构造方法中创建:
java
protected AbstractChannel(Channel parent, ChannelId id) {
this.parent = parent;
this.id = id;
unsafe = newUnsafe();
pipeline = newChannelPipeline();
}
newChannelPipeline() 默认返回 DefaultChannelPipeline:
java
protected DefaultChannelPipeline newChannelPipeline() {
return new DefaultChannelPipeline(this);
}
因此,Channel 完成对象构造时就已经拥有 Pipeline。此时 Channel 通常还没有注册到 EventLoop,也没有激活,但 Pipeline 已经存在。
2.2 初始化 HeadContext 和 TailContext
DefaultChannelPipeline 构造方法会创建两个固定节点:
java
protected DefaultChannelPipeline(Channel channel) {
this.channel = ObjectUtil.checkNotNull(channel, "channel");
succeededFuture = new SucceededChannelFuture(channel, null);
voidPromise = new VoidChannelPromise(channel, true);
tail = new TailContext(this);
head = new HeadContext(this);
head.next = tail;
tail.prev = head;
}
初始链表如下:
text
head <-> tail
加入业务 Handler 后变为:
text
head <-> context1 <-> context2 <-> ... <-> tail
HeadContext 和 TailContext 不只是链表哨兵,也是事件传播的边界节点。
HeadContext 同时实现入站和出站接口:
- 入站事件从
head开始向tail传播。 - 出站操作到达
head后,由HeadContext调用Unsafe,进入 Channel 的底层传输实现。
TailContext 只实现入站接口:
- 它接收入站方向最终没有被业务 Handler 处理的事件。
- 出站操作以
tail为查找起点,但TailContext自己不负责执行出站操作。
传播方向可以概括为:
text
入站事件:head -> 业务 Context -> tail
出站操作:tail -> 业务 Context -> head -> Unsafe
两个内部节点在构造时直接调用 setAddComplete(),不需要等待 handlerAdded() 回调, 所有这个两个节点有点特殊。
3. Handler 如何加入 Pipeline
3.1 添加操作统一进入 internalAdd()
addFirst()、addLast()、addBefore() 和 addAfter() 最终都进入 internalAdd()。它的主要步骤如下:
text
checkMultiplicity(handler)
-> 检查 Handler 实例是否允许再次添加
filterName(name, handler)
-> 生成或检查 Context 名称
newContext(group, name, handler)
-> 创建 DefaultChannelHandlerContext
addFirst0()/addLast0()/addBefore0()/addAfter0()
-> Context 插入双向链表
根据 Pipeline 注册状态
-> 立即执行或延迟执行 handlerAdded()
关键源码可以简化为:
java
private ChannelPipeline internalAdd(
EventExecutorGroup group,
String name,
ChannelHandler handler,
String baseName,
AddStrategy addStrategy) {
final AbstractChannelHandlerContext newCtx;
synchronized (this) {
checkMultiplicity(handler);
name = filterName(name, handler);
newCtx = newContext(group, name, handler);
// 根据 addStrategy 将 newCtx 插入双向链表
...
if (!registered) {
newCtx.setAddPending();
callHandlerCallbackLater(newCtx, true);
return this;
}
EventExecutor executor = newCtx.executor();
if (!executor.inEventLoop()) {
callHandlerAddedInEventLoop(newCtx, executor);
return this;
}
}
callHandlerAdded0(newCtx);
return this;
}
3.2 Context 插入链表不等于添加回调已经完成
以 addLast0() 为例:
java
private void addLast0(AbstractChannelHandlerContext newCtx) {
AbstractChannelHandlerContext prev = tail.prev;
newCtx.prev = prev;
newCtx.next = tail;
prev.next = newCtx;
tail.prev = newCtx;
}
这个方法只修改链表结构,不执行 Handler 的生命周期回调。因此要区分:
text
Context 插入链表
表示它已经成为 Pipeline 的结构节点
执行 handlerAdded()
表示 Handler 已进入可以参与事件处理的阶段
Pipeline 的结构修改放在同步块中完成。prev 和 next 使用 volatile 保证引用变化的可见性,但链表修改的原子性不能只依赖 volatile。
3.3 Handler 名称、复用和执行器
filterName() 的作用是处理 Context 名称:
- 调用方指定名称时,检查名称是否重复。
- 未指定名称时,根据 Handler 类型生成名称。
- 自动生成的名称可能包含编号,不一定只等于类名。
checkMultiplicity() 主要防止同一个非 @Sharable 的 ChannelHandlerAdapter 实例被重复添加:
- 未标记
@Sharable的实例不能重复加入 Pipeline。 @Sharable表示开发者声明该实例可以被多个 Pipeline 共享。@Sharable不会自动保证线程安全。Handler 内存在共享可变状态时,仍需自行保证并发安全。
newContext() 会为 Handler 选择执行器:
java
private AbstractChannelHandlerContext newContext(
EventExecutorGroup group, String name, ChannelHandler handler) {
return new DefaultChannelHandlerContext(
this, childExecutor(group), name, handler);
}
- 没有显式传入
EventExecutorGroup时,Context 默认使用 Channel 的 EventLoop。 - 显式传入 Group 时,Context 使用该 Group 中选出的
EventExecutor。
4. Context 的生命周期状态
4.1 handlerState 描述单个 Context
AbstractChannelHandlerContext 使用 handlerState 记录当前 Context 对应 Handler 的生命周期状态:
java
private static final int INIT = 0;
private static final int ADD_PENDING = 1;
private static final int ADD_COMPLETE = 2;
private static final int REMOVE_COMPLETE = 3;
private volatile int handlerState = INIT;
这些状态描述的是单个 Context,不是整个 Pipeline:
text
INIT
Context 刚创建,尚未安排 handlerAdded()
ADD_PENDING
Context 已进入链表,handlerAdded() 正在等待执行
ADD_COMPLETE
Context 已允许参与事件处理
REMOVE_COMPLETE
Context 已完成删除状态切换,不再参与正常事件处理
典型变化为:
text
INIT -> ADD_PENDING -> ADD_COMPLETE -> REMOVE_COMPLETE
如果 Handler 在 Channel 注册后由 EventLoop 线程直接添加,也可能从 INIT 直接进入 ADD_COMPLETE。
4.2 为什么先设置 ADD_COMPLETE 再调用 handlerAdded()
callHandlerAdded() 的顺序如下:
java
final void callHandlerAdded() throws Exception {
if (setAddComplete()) {
handler().handlerAdded(this);
}
}
Context 会先切换到 ADD_COMPLETE,再进入用户的 handlerAdded()。
原因是 Handler 可以在 handlerAdded() 内触发事件。如果状态仍是 ADD_PENDING,invokeHandler() 可能判断当前 Handler 尚未准备好,导致事件绕过它。
所以 ADD_COMPLETE 更准确的含义是"Context 已允许参与事件处理",不能机械理解成"handlerAdded() 已经执行返回"。
4.3 删除时为什么只对 ADD_COMPLETE 调用 handlerRemoved()
java
final void callHandlerRemoved() throws Exception {
try {
if (handlerState == ADD_COMPLETE) {
handler().handlerRemoved(this);
}
} finally {
setRemoved();
}
}
Netty 只在已经调用过 handlerAdded() 的前提下调用 handlerRemoved()。无论 handlerRemoved() 是否抛出异常,Context 最后都会进入 REMOVE_COMPLETE。
5. 注册前添加与注册后添加
5.1 注册前添加:先插入,再暂存回调
DefaultChannelPipeline 内部维护了一个 registered 字段,用于判断是否可以立即安排 Handler 回调。
Channel 尚未注册时:
text
Context 插入链表
-> setAddPending()
-> 创建 PendingHandlerAddedTask
-> 加入 pendingHandlerCallbackHead
-> 暂不执行 handlerAdded()
这样可以保证执行 handlerAdded() 时,Channel 已经绑定 EventLoop,Context 也能确定回调应在哪个执行器中运行。
5.2 Channel 注册推动待处理回调
Channel 注册成功后,AbstractChannel.AbstractUnsafe.register0() 会连接 Channel 与 Pipeline 的生命周期:
text
doRegister() 成功
-> AbstractChannel.registered = true
-> pipeline.invokeHandlerAddedIfNeeded()
-> safeSetSuccess(registerPromise)
-> pipeline.fireChannelRegistered()
-> 满足条件时 pipeline.fireChannelActive()
其中处理待执行回调的调用链为:
text
DefaultChannelPipeline.invokeHandlerAddedIfNeeded()
-> callHandlerAddedForAllHandlers()
-> PendingHandlerAddedTask.execute()
-> callHandlerAdded0()
-> AbstractChannelHandlerContext.callHandlerAdded()
-> setAddComplete()
-> ChannelHandler.handlerAdded()
invokeHandlerAddedIfNeeded() 只在第一次注册时处理一次待执行任务:
java
final void invokeHandlerAddedIfNeeded() {
assert channel.eventLoop().inEventLoop();
if (firstRegistration) {
firstRegistration = false;
callHandlerAddedForAllHandlers();
}
}
callHandlerAddedForAllHandlers() 会先在同步块内取出任务链表并把 Pipeline 的 registered 设置为 true,然后在同步块外执行任务。这样可以避免 Handler 在 handlerAdded() 中再次修改 Pipeline 时长期占用 Pipeline 的锁。
这里有两个同名但层次不同的状态:
AbstractChannel.registered:Channel 是否已经注册到 EventLoop 的 I/O 处理器。DefaultChannelPipeline.registered:Pipeline 是否已经进入可以立即安排 Handler 回调的阶段。
在首次注册过程中,Channel 的状态先更新,Pipeline 的状态随后在 callHandlerAddedForAllHandlers() 中更新。
DefaultChannelPipeline.registered 不是 Channel.isRegistered() 的实时镜像。它在首次注册时变为 true 后,不会因为 Channel 临时注销而恢复成 false。它的用途是标记 Pipeline 已经越过"注册前暂存 Handler 回调"的阶段;Channel 当前是否仍注册到 EventLoop,应继续查看 AbstractChannel.registered。
5.3 注册后添加:直接执行或提交到对应执行器
Channel 已经注册时:
text
当前线程属于 Context 的 EventExecutor
-> 直接执行 callHandlerAdded0()
当前线程不属于 Context 的 EventExecutor
-> setAddPending()
-> executor.execute(...)
-> 在对应 EventExecutor 中执行 callHandlerAdded0()
callHandlerAddedInEventLoop() 的名字容易造成误解。它不是在当前线程直接调用 handlerAdded(),而是把任务提交到目标 EventExecutor。
如果 Context 显式绑定了其他 EventExecutorGroup,PendingHandlerAddedTask.execute() 也可能只负责提交任务。register0() 不会同步等待这个任务执行完毕,但同一个有序执行器中的提交顺序会保证 handlerAdded() 先于后续事件回调。
6. ChannelInitializer 如何配置业务 Pipeline
6.1 ChannelInitializer 是一次性初始化 Handler
Bootstrap 通常不会直接一次性添加所有业务 Handler,而是先添加 ChannelInitializer。等 Channel 注册后,再由它调用用户实现的 initChannel() 配置业务 Pipeline。
ChannelInitializer.handlerAdded() 的核心逻辑如下:
java
public void handlerAdded(ChannelHandlerContext ctx) throws Exception {
if (ctx.channel().isRegistered()) {
if (initChannel(ctx)) {
removeState(ctx);
}
}
}
典型调用链为:
text
Bootstrap.init(channel)
-> pipeline.addLast(ChannelInitializer)
-> 此时 Channel 尚未注册,暂存 handlerAdded()
AbstractUnsafe.register0()
-> Channel.registered = true
-> 执行 ChannelInitializer.handlerAdded()
-> ChannelInitializer.initChannel()
-> 用户的 initChannel(channel)
-> 添加业务 Handler
-> 从 Pipeline 删除 ChannelInitializer
-> pipeline.fireChannelRegistered()
ChannelInitializer 可以理解为"对每个 Channel 只完成一次初始化任务的 Handler"。它自身标记为 @Sharable,同一个实例可以参与多个 Channel 的初始化;内部的 initMap 用于防止同一个 Channel 重入初始化。
6.2 服务端监听 Channel 的 Pipeline
ServerBootstrap.init() 会先向服务端监听 Channel 的 Pipeline 添加一个 ChannelInitializer:
text
ServerBootstrap.init(serverChannel)
-> serverChannel.pipeline().addLast(ChannelInitializer)
ChannelInitializer.initChannel(serverChannel)
-> 添加 config.handler()
-> 向 EventLoop 提交任务
-> 添加 ServerBootstrapAcceptor
config.handler() 属于服务端监听 Channel。ServerBootstrapAcceptor 负责处理 accept() 得到的服务端子 Channel。
6.3 服务端子 Channel 的 Pipeline
当服务端监听 Channel 读取到新连接时,ServerBootstrapAcceptor.channelRead() 执行:
text
接收服务端子 Channel
-> child.pipeline().addLast(childHandler)
-> 设置 childOptions 和 childAttrs
-> childGroup.register(child)
这里的 childHandler 通常也是用户配置的 ChannelInitializer。它在服务端子 Channel 注册前加入 Pipeline,注册成功后再执行 handlerAdded() 和 initChannel(),最终添加真正的业务 Handler。
因此需要区分:
handler()配置服务端监听 Channel 的 Handler。childHandler()配置每个服务端子 Channel 的 Handler。
客户端 Bootstrap.init() 的逻辑更直接:把 config.handler() 添加到客户端 Channel 的 Pipeline,然后注册 Channel。
7. 入站事件和出站操作的传播
7.1 入站事件从 HeadContext 开始
源码为每种入站事件提供具体方法,例如:
text
fireChannelRegistered()
fireChannelActive()
fireChannelRead()
fireChannelReadComplete()
fireChannelInactive()
fireChannelUnregistered()
以 channelRead 为例:
text
pipeline.fireChannelRead(msg)
-> HeadContext.channelRead()
-> head.fireChannelRead(msg)
-> findContextInbound(MASK_CHANNEL_READ)
-> 目标 Handler.channelRead(ctx, msg)
-> ctx.fireChannelRead(msg)
-> 继续沿 next 传播
-> TailContext
Handler 是否继续传播事件由 Handler 自己决定。自定义 Handler 重写入站方法后,如果既不调用 ctx.fireChannelRead(msg),也不消费或释放消息,后续 Handler 将收不到事件,还可能造成引用计数对象泄漏。
7.2 出站操作从 TailContext 开始
出站操作包括:
text
bind()
connect()
read()
write()
flush()
close()
deregister()
以 write 为例:
text
channel.write(msg)
-> pipeline.write(msg)
-> tail.write(msg)
-> findContextOutbound(MASK_WRITE)
-> 目标 Handler.write(ctx, msg, promise)
-> ctx.write(msg, promise)
-> 继续沿 prev 传播
-> HeadContext.write()
-> Unsafe.write()
源码中不存在通用的 in() 或 out() 方法。各种事件的方法结构相似,但阅读时应选择真实方法跟踪,不能把概念性伪代码当成源码调用。
7.3 executionMask 优化了什么
executionMask 由 ChannelHandlerMask.mask(handlerClass) 计算。一个 int 的不同位表示 Handler 是否需要处理某种事件。
Netty 会按 Handler 类型缓存计算结果,避免在事件传播的高频路径中反复通过反射判断 Handler 是否处理某个方法。
查找 Context 时仍然会沿双向链表逐个检查节点:
java
private AbstractChannelHandlerContext findContextInbound(int mask) {
AbstractChannelHandlerContext ctx = this;
EventExecutor currentExecutor = executor();
do {
ctx = ctx.next;
} while (skipContext(ctx, currentExecutor, mask, MASK_ONLY_INBOUND));
return ctx;
}
private AbstractChannelHandlerContext findContextOutbound(int mask) {
AbstractChannelHandlerContext ctx = this;
EventExecutor currentExecutor = executor();
do {
ctx = ctx.prev;
} while (skipContext(ctx, currentExecutor, mask, MASK_ONLY_OUTBOUND));
return ctx;
}
因此,executionMask 不是把链表变成可以直接定位节点的索引。它的优化点是:
- 跳过不处理当前事件的 Handler 方法调用。
- 在满足顺序约束时,避免不必要的线程切换。
Pipeline 仍然是标准的责任链结构,只是在节点选择上做了高频路径优化。
7.4 为什么 skipContext() 还要比较 EventExecutor
java
private static boolean skipContext(
AbstractChannelHandlerContext ctx,
EventExecutor currentExecutor,
int mask,
int onlyMask) {
return (ctx.executionMask & (onlyMask | mask)) == 0 ||
(ctx.executor() == currentExecutor &&
(ctx.executionMask & mask) == 0);
}
可以分成两种情况理解:
- 目标 Context 与本次传播方向和事件完全无关,可以直接跳过。
- 目标 Context 与当前传播过程使用同一个执行器,并且不处理本次事件,可以继续查找。
如果目标 Context 使用不同的执行器,即使它不处理当前具体事件,也不能总是在当前线程直接跨过。事件可能仍需先提交到目标执行器,再从那里继续传播,以维持同一 Channel 上不同事件之间的执行顺序。
8. 运行期间删除和替换 Handler
8.1 remove() 先修改链表,再安排 handlerRemoved()
DefaultChannelPipeline.remove() 的核心逻辑为:
java
private AbstractChannelHandlerContext remove(
final AbstractChannelHandlerContext ctx) {
assert ctx != head && ctx != tail;
synchronized (this) {
atomicRemoveFromHandlerList(ctx);
if (!registered) {
callHandlerCallbackLater(ctx, false);
return ctx;
}
EventExecutor executor = ctx.executor();
if (!executor.inEventLoop()) {
executor.execute(() -> callHandlerRemoved0(ctx));
return ctx;
}
}
callHandlerRemoved0(ctx);
return ctx;
}
删除也要区分结构变化和生命周期回调:
text
atomicRemoveFromHandlerList(ctx)
-> Context 立即脱离双向链表
callHandlerRemoved0(ctx)
-> 在 Context 对应的 EventExecutor 中执行 handlerRemoved()
如果 Channel 尚未注册,删除回调也会进入待处理任务链表。假设某个 Handler 在注册前先添加再删除,在默认 EventLoop 或其他有序 EventExecutor 中,注册时会按任务提交顺序执行:
text
handlerAdded()
-> handlerRemoved()
如果显式绑定的是无序执行器,不能再假设两个任务严格串行执行。此时 Context 状态仍会阻止在没有完成添加状态的情况下调用 handlerRemoved(),但回调的具体并发时机需要由所选执行器负责。
8.2 replace() 为什么先添加新 Handler
replace() 会先使用 replace0() 把旧 Context 替换成新 Context,然后安排生命周期回调:
text
replace0(oldCtx, newCtx)
-> 新 Context 接管旧 Context 的链表位置
callHandlerAdded0(newCtx)
-> 新 Handler 先完成添加回调
callHandlerRemoved0(oldCtx)
-> 旧 Handler 再完成删除回调
必须先执行新 Handler 的 handlerAdded()。因为旧 Handler 可能在 handlerRemoved() 中继续传播 channelRead() 或 flush(),这些事件到达新 Handler 前,新 Handler 必须已经进入可执行状态。
replace0() 还会让旧 Context 的 prev 和 next 指向新 Context。这样旧 Handler 中尚未结束的转发操作会进入新 Context,而不是继续使用已经失效的旧链表位置。
8.3 handlerAdded() 异常时的处理
如果 handlerAdded() 抛出异常,callHandlerAdded0() 会尝试:
text
从链表删除 Context
-> 调用 handlerRemoved()
-> 传播 ChannelPipelineException
因此,Handler 添加失败后通常不会继续残留在 Pipeline 中。
9. Channel 注销如何销毁 Pipeline
9.1 channelUnregistered() 是销毁入口
Channel 关闭和注销的主线为:
text
AbstractUnsafe.deregister()
-> doDeregister()
-> pipeline.fireChannelInactive()
-> AbstractChannel.registered = false
-> pipeline.fireChannelUnregistered()
fireChannelUnregistered() 从 HeadContext 开始,它属于入站事件,会先沿 Pipeline 向 TailContext 传播。
真正判断是否销毁业务 Context 的代码位于 HeadContext.channelUnregistered(),不是 TailContext:
java
public void channelUnregistered(ChannelHandlerContext ctx) {
ctx.fireChannelUnregistered();
if (!channel.isOpen()) {
destroy();
}
}
顺序是先传播 channelUnregistered(),再开始销毁。这保证业务 Handler 有机会先收到注销事件,之后才执行 handlerRemoved()。
还要区分两种注销:
text
Channel 仍然打开,只是暂时注销
-> 传播 channelUnregistered()
-> 不执行 destroy()
-> 后续仍可能重新注册
Channel 已经关闭并注销
-> 传播 channelUnregistered()
-> 执行 destroy()
-> 删除所有业务 Context
9.2 destroyUp() 不负责删除节点
destroy() 从 head.next 开始执行 destroyUp():
java
private synchronized void destroy() {
destroyUp(head.next, false);
}
destroyUp() 从 head 方向走向 tail,但这个阶段不修改链表:
text
head.next
-> 沿 next 遍历业务 Context
-> 遇到不同 EventExecutor 时提交继续遍历的任务
-> 到达 tail
-> 调用 destroyDown(tail.prev, ...)
它的目的是依次进入各 Context 对应的执行器。在默认 EventLoop 或其他有序执行器中,之前已经提交的事件任务会排在销毁任务之前,避免 Handler 还在处理事件时就被提前删除。如果显式绑定无序执行器,则不能依赖这种任务先后关系。
源码中的:
java
ctx = ctx.next;
只是让局部变量移动到下一个节点,不会断开当前节点的引用,也不会修改 Pipeline 链表。
9.3 destroyDown() 才真正删除节点
到达 tail 后,destroyDown() 从 tail.prev 开始反向删除:
java
private void destroyDown(
Thread currentThread,
AbstractChannelHandlerContext ctx,
boolean inEventLoop) {
final AbstractChannelHandlerContext head = this.head;
for (;;) {
if (ctx == head) {
break;
}
EventExecutor executor = ctx.executor();
if (inEventLoop || executor.inEventLoop(currentThread)) {
atomicRemoveFromHandlerList(ctx);
callHandlerRemoved0(ctx);
} else {
AbstractChannelHandlerContext finalCtx = ctx;
executor.execute(() ->
destroyDown(Thread.currentThread(), finalCtx, true));
break;
}
ctx = ctx.prev;
inEventLoop = false;
}
}
实际顺序为:
text
destroyUp()
从 head 向 tail 遍历,只处理执行器切换和任务顺序
destroyDown()
从 tail 向 head 遍历
-> atomicRemoveFromHandlerList(ctx)
-> handlerRemoved()
Handler 按从尾到头的顺序删除。每个 Context 的删除回调都在它所属的 EventExecutor 中执行。
这里所说的"销毁 Pipeline",主要是删除业务 Context 并执行 handlerRemoved()。head、tail 和 Pipeline 对象仍由 Channel 引用,最终随 Channel 一起由 GC 回收。
10. 完整生命周期与回调顺序
把前面的流程串起来,可以得到:
text
创建 Channel
-> 创建 DefaultChannelPipeline
-> 创建 HeadContext 和 TailContext
Bootstrap 初始化
-> 添加 ChannelInitializer 或其他 Handler
-> Context 插入链表
-> Context 进入 ADD_PENDING
Channel 首次注册
-> Channel.registered = true
-> Pipeline.registered = true
-> handlerAdded()
-> ChannelInitializer.initChannel()
-> 添加业务 Handler
-> 删除 ChannelInitializer
-> channelRegistered()
-> 满足条件时 channelActive()
Channel 运行期间
-> 入站事件从 head 向 tail 传播
-> 出站操作从 tail 向 head 传播
-> 可以动态 add/remove/replace Handler
Channel 关闭并注销
-> channelInactive()
-> channelUnregistered()
-> HeadContext 判断 Channel 已关闭
-> destroyUp()
-> destroyDown()
-> 业务 Handler 按 tail 到 head 的顺序执行 handlerRemoved()
对于普通 Handler,可以记住以下基本顺序:
text
handlerAdded()
-> channelRegistered()
-> channelActive()
-> channelRead()/write()/其他事件
-> channelInactive()
-> channelUnregistered()
-> handlerRemoved()
这个顺序描述的是正常的首次注册、激活和关闭流程。动态添加、动态删除、重新注册以及绑定其他 EventExecutorGroup 时,具体时机会有所变化,但 Netty 会通过 Context 状态和有序任务提交维持单个 Context 的合法回调顺序。