Netty Pipeline 初始化流程详解
前言
在使用 Netty 开发网络应用时,Pipeline(管道)是一个非常核心的概念。它就像一条流水线,负责处理进出的数据。但你有没有想过,当我们调用 b.handler(new MyChannelInitializer()) 后,Netty 是如何把这个 Handler 添加到 Pipeline 中的?又是在什么时候真正完成初始化的呢?
本文将带你深入 Netty 源码,一步步揭开 Pipeline 初始化的神秘面纱。即使你是 Netty 初学者,也能轻松理解整个流程。
什么是 Pipeline?
在深入源码之前,我们先简单了解一下 Pipeline 的概念:
Pipeline 是 Netty 中的责任链模式实现,它维护了一个由多个 ChannelHandler 组成的双向链表。每个 Channel 都有自己的 Pipeline,用于处理入站(Inbound)和出站(Outbound)事件。
Pipeline 的基本结构如下:
head <-> handler1 <-> handler2 <-> ... <-> tail
其中 head 和 tail 是 Netty 自动创建的两个特殊节点,我们添加的 Handler 都会插入到它们之间。
整体流程概览
Pipeline 的初始化主要分为两个阶段:
- 准备阶段(Init):在 Channel 创建时,将 ChannelInitializer 添加到 Pipeline,但此时还不会真正执行初始化逻辑
- 执行阶段(Register):在 Channel 注册到 EventLoop 后,触发所有 Handler 的
handlerAdded()方法,完成真正的初始化
为什么要分两个阶段呢?因为在 Channel 还没有注册到 EventLoop 之前,很多操作是不安全的。Netty 采用了一种"延迟初始化"的策略,先把需要执行的任务记录下来,等到合适的时机再统一执行。
接下来,我们从客户端启动代码开始,一步步追踪源码。
第一阶段:准备阶段(Init)
1. 客户端启动入口
让我们从一个典型的 Netty 客户端启动代码开始:
private void connect(String inetHost, int inetPort) {
EventLoopGroup workerGroup = new NioEventLoopGroup();
try {
Bootstrap b = new Bootstrap();
b.group(workerGroup);
b.channel(NioSocketChannel.class);
b.option(ChannelOption.AUTO_READ, true);
b.handler(new MyChannelInitializer()); // 设置 Handler
// 关键:调用 connect 方法
ChannelFuture f = b.connect(inetHost, inetPort).sync();
f.channel().closeFuture().sync();
} catch (InterruptedException e) {
e.printStackTrace();
} finally {
workerGroup.shutdownGracefully();
}
}
这里的关键是 b.connect(inetHost, inetPort) 这行代码,它会触发整个 Pipeline 的初始化流程。
2. connect() 方法调用链
当我们调用 b.connect() 时,会经过以下调用链:
// Bootstrap.connect(String, int)
public ChannelFuture connect(String inetHost, int inetPort) {
return connect(InetSocketAddress.createUnresolved(inetHost, inetPort));
}
// Bootstrap.connect(SocketAddress)
public ChannelFuture connect(SocketAddress remoteAddress) {
if (remoteAddress == null) {
throw new NullPointerException("remoteAddress");
}
validate();
return doResolveAndConnect(remoteAddress, config.localAddress());
}
// Bootstrap.doResolveAndConnect()
private ChannelFuture doResolveAndConnect(final SocketAddress remoteAddress,
final SocketAddress localAddress) {
// 关键方法:初始化并注册 Channel
final ChannelFuture regFuture = initAndRegister();
// ... 省略其他代码
}
这个调用链最终会走到 initAndRegister() 方法,这是 Pipeline 初始化的核心入口。
3. initAndRegister() - 初始化和注册的入口
final ChannelFuture initAndRegister() {
Channel channel = null;
try {
// 1. 创建 Channel 实例(NioSocketChannel)
channel = channelFactory.newChannel();
// 2. 初始化 Channel(重点关注这里)
init(channel);
} catch (Throwable t) {
// ... 异常处理
}
// 3. 将 Channel 注册到 EventLoop(稍后分析)
ChannelFuture regFuture = config().group().register(channel);
return regFuture;
}
这个方法做了三件事:
- 创建 Channel 实例
- 调用
init()方法初始化 Channel - 将 Channel 注册到 EventLoop
我们重点关注第 2 步的 init() 方法。
4. init() - 将 ChannelInitializer 添加到 Pipeline
@Override
void init(Channel channel) throws Exception {
ChannelPipeline p = channel.pipeline();
// 将我们设置的 Handler(ChannelInitializer)添加到 Pipeline
p.addLast(new ChannelInitializer<Channel>() {
@Override
public void initChannel(final Channel ch) throws Exception {
final ChannelPipeline pipeline = ch.pipeline();
ChannelHandler handler = config.handler();
if (handler != null) {
pipeline.addLast(handler);
}
// ... 其他初始化逻辑
}
});
}
关键点:这里调用了 p.addLast() 方法,将 ChannelInitializer 添加到 Pipeline 中。
此时 Pipeline 的状态变为:
head <-> ChannelInitializer <-> tail
但是,ChannelInitializer 的 initChannel() 方法还没有被调用!这就是"延迟初始化"的体现。
5. addLast() - 添加 Handler 的核心逻辑
让我们深入 addLast() 方法,看看 Netty 是如何处理 Handler 添加的:
@Override
public final ChannelPipeline addLast(ChannelHandler... handlers) {
return addLast(null, handlers);
}
public final ChannelPipeline addLast(EventExecutorGroup executor, ChannelHandler... handlers) {
for (ChannelHandler h: handlers) {
if (h == null) {
break;
}
// 遍历每个 Handler,逐个添加
addLast(executor, null, h);
}
return this;
}
最终会调用到这个重载方法:
@Override
public final ChannelPipeline addLast(EventExecutorGroup group, String name, ChannelHandler handler) {
final AbstractChannelHandlerContext newCtx;
synchronized (this) {
// 检查 Handler 是否可以被多次添加
checkMultiplicity(handler);
// 步骤1:将 Handler 包装成 Context
newCtx = newContext(group, filterName(name, handler), handler);
// 步骤2:将 Context 添加到链表中
addLast0(newCtx);
// 步骤3:关键判断 - 如果还没有注册,创建待处理任务
if (!registered) {
newCtx.setAddPending();
// 创建 PendingHandlerAddedTask 并加入队列
callHandlerCallbackLater(newCtx, true);
return this;
}
// 如果已经注册,直接调用 handlerAdded
EventExecutor executor = newCtx.executor();
if (!executor.inEventLoop()) {
// ... 省略
}
callHandlerAdded0(newCtx);
}
return this;
}
这个方法是 Pipeline 初始化的核心,让我们逐步分析:
步骤1:newContext() - 包装 Handler
newCtx = newContext(group, filterName(name, handler), handler);
作用:将 ChannelHandler 包装成 AbstractChannelHandlerContext。
为什么要包装呢?因为 Pipeline 实际上是一个 Context 的双向链表,而不是 Handler 的链表。每个 Context 都持有一个 Handler 的引用,同时还包含了前后节点的引用、执行器等信息。
可以这样理解:
- Handler:业务逻辑处理器(我们编写的代码)
- Context:Handler 的容器,包含了 Handler 以及它在链表中的位置信息
步骤2:addLast0() - 添加到链表
addLast0(newCtx);
作用:将新创建的 Context 添加到 Pipeline 的双向链表末尾(tail 之前)。
这个方法会修改链表的指针,将新节点插入到 tail 之前:
head <-> ... <-> newCtx <-> tail
步骤3:callHandlerCallbackLater() - 延迟回调
if (!registered) {
newCtx.setAddPending();
callHandlerCallbackLater(newCtx, true);
return this;
}
关键判断:if (!registered)
这里判断 Channel 是否已经注册到 EventLoop。在我们的场景中,此时 Channel 还没有注册,所以会进入这个分支。
callHandlerCallbackLater() 方法的作用是:将 Handler 的初始化任务延迟到注册完成后再执行。
6. callHandlerCallbackLater() - 创建待处理任务
让我们看看这个方法的实现:
private void callHandlerCallbackLater(AbstractChannelHandlerContext ctx, boolean added) {
assert !registered;
// 根据 added 参数创建不同的任务
// added = true:创建 PendingHandlerAddedTask
// added = false:创建 PendingHandlerRemovedTask
PendingHandlerCallback task = added ?
new PendingHandlerAddedTask(ctx) :
new PendingHandlerRemovedTask(ctx);
// 将任务加入到待处理队列
PendingHandlerCallback pending = pendingHandlerCallbackHead;
if (pending == null) {
// 队列为空,设置为头节点
pendingHandlerCallbackHead = task;
} else {
// 队列不为空,添加到队列尾部
while (pending.next != null) {
pending = pending.next;
}
pending.next = task;
}
}
核心思想:
这个方法创建了一个 PendingHandlerAddedTask 任务,并将它加入到一个待处理任务队列中。这个队列是一个简单的单向链表,用 pendingHandlerCallbackHead 作为头节点。
为什么要用任务队列?
因为在 Channel 还没有注册到 EventLoop 之前,很多操作是不安全的。比如:
- 无法确定在哪个线程执行
- EventLoop 还没有准备好
- 可能会导致线程安全问题
所以 Netty 采用了一种巧妙的设计:先把需要执行的操作记录下来(创建任务),等到 Channel 注册完成后,再统一执行这些任务。
小结:
到这里,准备阶段就完成了。此时的状态是:
- ChannelInitializer 已经被添加到 Pipeline 链表中
- 一个 PendingHandlerAddedTask 任务被创建并加入到待处理队列
- 但是 ChannelInitializer 的
initChannel()方法还没有被调用
Pipeline 状态:
head <-> ChannelInitializer(待初始化) <-> tail
待处理任务队列:
pendingHandlerCallbackHead -> PendingHandlerAddedTask(ChannelInitializer)
接下来,我们进入第二阶段:执行阶段。
第二阶段:执行阶段(Register)
7. register() - 注册 Channel 到 EventLoop
回到 initAndRegister() 方法,在调用完 init() 后,会继续执行注册操作:
final ChannelFuture initAndRegister() {
Channel channel = null;
try {
channel = channelFactory.newChannel();
init(channel); // 准备阶段完成
} catch (Throwable t) {
// ...
}
// 执行阶段开始:注册 Channel
ChannelFuture regFuture = config().group().register(channel);
return regFuture;
}
让我们追踪 register() 方法的调用链:
// MultithreadEventLoopGroup.register()
@Override
public ChannelFuture register(Channel channel) {
return register(new DefaultChannelPromise(channel, this));
}
// SingleThreadEventLoop.register()
@Override
public ChannelFuture register(final ChannelPromise promise) {
ObjectUtil.checkNotNull(promise, "promise");
// 调用 Channel 的 Unsafe 进行注册
promise.channel().unsafe().register(this, promise);
return promise;
}
// AbstractChannel.AbstractUnsafe.register()
@Override
public final void register(EventLoop eventLoop, final ChannelPromise promise) {
// ... 省略一些检查和设置
// 判断是否在 EventLoop 线程中
if (eventLoop.inEventLoop()) {
register0(promise);
} else {
// 如果不在,提交任务到 EventLoop
eventLoop.execute(new Runnable() {
@Override
public void run() {
register0(promise);
}
});
}
}
最终会调用到 register0() 方法。
8. register0() - 真正的注册逻辑
private void register0(ChannelPromise promise) {
try {
// 确保 Channel 还是打开状态
if (!promise.setUncancellable() || !ensureOpen(promise)) {
return;
}
boolean firstRegistration = neverRegistered;
// 执行底层的注册操作(将 Channel 注册到 Selector)
doRegister();
neverRegistered = false;
registered = true;
// 关键:调用 Pipeline 的 invokeHandlerAddedIfNeeded()
pipeline.invokeHandlerAddedIfNeeded();
// 触发 channelRegistered 事件
safeSetSuccess(promise);
pipeline.fireChannelRegistered();
// ... 其他逻辑
} catch (Throwable t) {
// ... 异常处理
}
}
关键代码:pipeline.invokeHandlerAddedIfNeeded()
这行代码会触发所有待处理任务的执行,也就是我们在准备阶段创建的 PendingHandlerAddedTask。
9. invokeHandlerAddedIfNeeded() - 触发待处理任务
final void invokeHandlerAddedIfNeeded() {
assert channel.eventLoop().inEventLoop();
if (firstRegistration) {
firstRegistration = false;
// 调用所有待处理的 handlerAdded 回调
callHandlerAddedForAllHandlers();
}
}
作用:检查是否是第一次注册,如果是,则调用 callHandlerAddedForAllHandlers() 执行所有待处理任务。
10. callHandlerAddedForAllHandlers() - 执行所有待处理任务
private void callHandlerAddedForAllHandlers() {
final PendingHandlerCallback pendingHandlerCallbackHead;
synchronized (this) {
assert !registered;
// 标记为已注册
registered = true;
// 获取待处理任务队列的头节点
pendingHandlerCallbackHead = this.pendingHandlerCallbackHead;
// 清空队列(帮助 GC)
this.pendingHandlerCallbackHead = null;
}
// 遍历任务队列,执行每个任务
PendingHandlerCallback task = pendingHandlerCallbackHead;
while (task != null) {
task.execute(); // 执行任务
task = task.next; // 移动到下一个任务
}
}
核心逻辑:
- 将
registered标记设置为true,表示 Channel 已经注册 - 获取待处理任务队列的头节点
- 遍历队列,依次执行每个任务的
execute()方法
这里的 task 就是我们在准备阶段创建的 PendingHandlerAddedTask。
11. PendingHandlerAddedTask.execute() - 执行添加任务
让我们看看 PendingHandlerAddedTask 的 execute() 方法:
@Override
void execute() {
EventExecutor executor = ctx.executor();
// 判断是否在 EventLoop 线程中
if (executor.inEventLoop()) {
// 在 EventLoop 线程中,直接调用
callHandlerAdded0(ctx);
} else {
// 不在 EventLoop 线程中,提交任务
try {
executor.execute(this);
} catch (RejectedExecutionException e) {
if (logger.isWarnEnabled()) {
logger.warn(
"Can't invoke handlerAdded() as the EventExecutor {} rejected it, removing handler {}.",
executor, ctx.name(), e);
}
remove0(ctx);
ctx.setRemoved();
}
}
}
作用:
这个方法确保 callHandlerAdded0() 在正确的线程(EventLoop 线程)中执行。如果当前已经在 EventLoop 线程中,就直接调用;否则,将任务提交到 EventLoop 中执行。
在我们的场景中,由于 register0() 已经在 EventLoop 线程中执行,所以会直接调用 callHandlerAdded0(ctx)。
12. callHandlerAdded0() - 调用 handlerAdded 回调
private void callHandlerAdded0(final AbstractChannelHandlerContext ctx) {
try {
// 调用 Context 的 callHandlerAdded 方法
ctx.callHandlerAdded();
} catch (Throwable t) {
boolean removed = false;
try {
// 如果出现异常,移除这个 Handler
remove0(ctx);
ctx.callHandlerRemoved();
removed = true;
} catch (Throwable t2) {
if (logger.isWarnEnabled()) {
logger.warn("Failed to remove a handler: " + ctx.name(), t2);
}
}
// 触发异常事件
if (removed) {
fireExceptionCaught(new ChannelPipelineException(
ctx.handler().getClass().getName() +
".handlerAdded() has thrown an exception; removed.", t));
} else {
fireExceptionCaught(new ChannelPipelineException(
ctx.handler().getClass().getName() +
".handlerAdded() has thrown an exception; also failed to remove.", t));
}
}
}
作用:
调用 Context 的 callHandlerAdded() 方法,并处理可能出现的异常。如果 handlerAdded() 方法抛出异常,会自动将这个 Handler 从 Pipeline 中移除。
13. AbstractChannelHandlerContext.callHandlerAdded() - 最终调用
final void callHandlerAdded() throws Exception {
// 设置状态为 ADD_COMPLETE
// 这个方法会检查状态,确保 handlerAdded 只被调用一次
if (setAddComplete()) {
// 调用 Handler 的 handlerAdded 方法
handler().handlerAdded(this);
}
}
关键点:handler().handlerAdded(this)
这行代码会调用当前 Context 持有的 Handler 的 handlerAdded() 方法。
重要说明:这里调用的是 Bootstrap.init() 方法中创建的匿名 ChannelInitializer(Initializer-A)的 handlerAdded() 方法,而不是我们自定义的 MyChannelInitializer。
为什么呢?因为:
- 在准备阶段,
p.addLast(Initializer-A)创建了 ctx-A - ctx-A 持有的 handler 引用指向 Initializer-A
- PendingHandlerAddedTask 持有的是 ctx-A 的引用
- 所以这里的
handler()返回的是 Initializer-A
Initializer-A 的 handlerAdded() 方法会:
- 调用 Initializer-A 重写的
initChannel()方法 - 在
initChannel()中,将我们的 MyChannelInitializer 添加到 Pipeline - MyChannelInitializer 被添加后,也会触发它的
handlerAdded()方法 - 最终调用到我们重写的
initChannel()方法
接下来,我们详细分析这个调用流程。
深入理解:initChannel() 方法的调用机制
核心问题回顾
在第 13 步中,我们看到 handler().handlerAdded(this) 被调用。这里有一个非常重要的问题需要理解:
当 handler().handlerAdded(this) 被调用时,调用的是哪个 ChannelInitializer 实例的方法?
答案是:调用的是 Bootstrap.init() 方法中创建的匿名 ChannelInitializer(Initializer-A)实例的 handlerAdded() 方法。
理解关键:Context 持有的是哪个 Handler?
回顾 Pipeline 初始化的准备阶段
在 Bootstrap.init() 方法中,Netty 创建了一个匿名 ChannelInitializer:
@Override
void init(Channel channel) throws Exception {
ChannelPipeline p = channel.pipeline();
// 创建匿名 ChannelInitializer(我们称它为 Initializer-A)
p.addLast(new ChannelInitializer<Channel>() {
@Override
public void initChannel(final Channel ch) throws Exception {
final ChannelPipeline pipeline = ch.pipeline();
// 获取用户设置的 handler(MyChannelInitializer)
ChannelHandler handler = config.handler();
if (handler != null) {
// 将 MyChannelInitializer 添加到 Pipeline
pipeline.addLast(handler);
}
}
});
}
关键点:addLast() 做了什么?
当调用 p.addLast(new ChannelInitializer<Channel>() {...}) 时:
- 创建了一个新的 Context(我们称它为 ctx-A)
- ctx-A 持有的 handler 引用指向这个匿名 ChannelInitializer(Initializer-A)
- ctx-A 被添加到 Pipeline 链表中
- 创建了一个 PendingHandlerAddedTask,持有 ctx-A 的引用
此时的状态:
Pipeline: head <-> ctx-A(持有 Initializer-A) <-> tail
待处理任务: PendingHandlerAddedTask(ctx-A)
执行阶段:调用 handlerAdded
当 Channel 注册完成后,会执行待处理任务:
// PendingHandlerAddedTask.execute()
void execute() {
callHandlerAdded0(ctx); // 这里的 ctx 就是 ctx-A
}
// callHandlerAdded0
private void callHandlerAdded0(final AbstractChannelHandlerContext ctx) {
ctx.callHandlerAdded(); // 调用 ctx-A 的 callHandlerAdded
}
// AbstractChannelHandlerContext.callHandlerAdded
final void callHandlerAdded() throws Exception {
if (setAddComplete()) {
// 这里的 handler() 返回的是 ctx-A 持有的 handler
// 也就是 Initializer-A(匿名 ChannelInitializer)
handler().handlerAdded(this);
}
}
关键理解:
handler()方法返回的是当前 Context 持有的 Handler 引用- ctx-A 持有的是 Initializer-A(匿名 ChannelInitializer)
- 所以
handler().handlerAdded(this)调用的是 Initializer-A 的 handlerAdded() 方法
Initializer-A 的完整调用流程
现在让我们完整地追踪 Initializer-A 的 handlerAdded() 方法是如何最终调用到我们自定义的 MyChannelInitializer 的 initChannel() 方法的。
步骤1:调用 Initializer-A 的 handlerAdded()
// ChannelInitializer.handlerAdded()
@Override
public void handlerAdded(ChannelHandlerContext ctx) throws Exception {
// 检查 Channel 是否已经注册
if (ctx.channel().isRegistered()) {
// 调用 initChannel(ctx)
if (initChannel(ctx)) {
// 初始化成功后,移除自己
removeState(ctx);
}
}
}
作用:
这个方法是 ChannelInitializer 的核心方法,它会:
- 检查 Channel 是否已经注册(此时已经注册)
- 调用
initChannel(ctx)方法 - 初始化完成后,将自己从 Pipeline 中移除
步骤2:调用 initChannel(ChannelHandlerContext)
// ChannelInitializer.initChannel(ChannelHandlerContext)
private boolean initChannel(ChannelHandlerContext ctx) throws Exception {
// 使用 Set 确保每个 Context 只初始化一次
if (initMap.add(ctx)) {
try {
// 关键:调用抽象方法 initChannel(Channel)
// 由于多态,这里会调用子类(Initializer-A)重写的版本
initChannel((C) ctx.channel());
} catch (Throwable cause) {
// 如果初始化过程中出现异常,触发异常处理
exceptionCaught(ctx, cause);
} finally {
// 无论成功还是失败,都要将 ChannelInitializer 从 Pipeline 中移除
ChannelPipeline pipeline = ctx.pipeline();
if (pipeline.context(this) != null) {
pipeline.remove(this);
}
}
return true;
}
return false;
}
作用:
这个方法做了三件重要的事:
- 使用
initMap确保每个 Context 只初始化一次(防止重复初始化) - 调用抽象方法
initChannel(Channel),由于 Java 的多态机制,会调用 Initializer-A 重写的版本 - 在 finally 块中移除 ChannelInitializer,确保它只执行一次
为什么要移除?
ChannelInitializer 是一个"一次性"的 Handler,它的唯一作用就是在合适的时机初始化 Pipeline。一旦初始化完成,它就没有存在的必要了,所以要从 Pipeline 中移除。
步骤3:调用 Initializer-A 重写的 initChannel(Channel)
// 这是在 Bootstrap.init() 中定义的匿名内部类
new ChannelInitializer<Channel>() {
@Override
public void initChannel(final Channel ch) throws Exception {
final ChannelPipeline pipeline = ch.pipeline();
// 获取用户设置的 handler(MyChannelInitializer)
ChannelHandler handler = config.handler();
if (handler != null) {
// 将 MyChannelInitializer 添加到 Pipeline
pipeline.addLast(handler);
}
// 注意:这里可能还有其他逻辑,比如添加 ServerBootstrapAcceptor
// 但对于客户端来说,主要就是添加用户的 handler
}
}
作用:
这是 Initializer-A 重写的 initChannel() 方法,它的作用是:
- 获取用户通过
b.handler()设置的 Handler(也就是 MyChannelInitializer) - 将 MyChannelInitializer 添加到 Pipeline 中
关键理解:
config.handler()返回的是我们在客户端启动代码中设置的new MyChannelInitializer()- 这里调用
pipeline.addLast(handler)会将 MyChannelInitializer 添加到 Pipeline
此时 Pipeline 的状态变化:
之前:head <-> Initializer-A <-> tail
之后:head <-> MyChannelInitializer <-> tail
(Initializer-A 在 finally 块中被移除)
MyChannelInitializer 的初始化流程
现在,MyChannelInitializer 已经被添加到 Pipeline 中了。接下来,它也会经历同样的初始化流程。
步骤4:addLast(MyChannelInitializer)
当调用 pipeline.addLast(handler) 时(这里的 handler 是 MyChannelInitializer):
@Override
public final ChannelPipeline addLast(ChannelHandler... handlers) {
return addLast(null, handlers);
}
public final ChannelPipeline addLast(EventExecutorGroup executor, ChannelHandler... handlers) {
if (handlers == null) {
throw new NullPointerException("handlers");
}
for (ChannelHandler h: handlers) {
if (h == null) {
break;
}
// 遍历每个 Handler,逐个添加
addLast(executor, null, h);
}
return this;
}
最终会调用到:
@Override
public final ChannelPipeline addLast(EventExecutorGroup group, String name, ChannelHandler handler) {
final AbstractChannelHandlerContext newCtx;
synchronized (this) {
// 检查 Handler 是否可以被多次添加
checkMultiplicity(handler);
// 将 MyChannelInitializer 包装成 Context(我们称它为 ctx-B)
newCtx = newContext(group, filterName(name, handler), handler);
// 将 ctx-B 添加到 Pipeline 链表中
addLast0(newCtx);
// 关键判断:此时 Channel 已经注册(registered = true)
if (!registered) {
// 如果还没注册,创建待处理任务
newCtx.setAddPending();
callHandlerCallbackLater(newCtx, true);
return this;
}
// 因为已经注册,所以直接调用 handlerAdded
EventExecutor executor = newCtx.executor();
if (!executor.inEventLoop()) {
// 如果不在 EventLoop 线程中,提交任务
callHandlerAddedInEventLoop(newCtx, executor);
return this;
}
}
// 在 EventLoop 线程中,直接调用
callHandlerAdded0(newCtx);
return this;
}
关键区别:
与 Initializer-A 不同,此时 Channel 已经注册(registered = true),所以不会创建待处理任务,而是直接调用 callHandlerAdded0(newCtx)。
步骤5:调用 callHandlerAdded0(ctx-B)
private void callHandlerAdded0(final AbstractChannelHandlerContext ctx) {
try {
// 调用 ctx-B 的 callHandlerAdded
ctx.callHandlerAdded();
} catch (Throwable t) {
// 异常处理:如果出现异常,移除这个 Handler
boolean removed = false;
try {
remove0(ctx);
ctx.callHandlerRemoved();
removed = true;
} catch (Throwable t2) {
if (logger.isWarnEnabled()) {
logger.warn("Failed to remove a handler: " + ctx.name(), t2);
}
}
// 触发异常事件
if (removed) {
fireExceptionCaught(new ChannelPipelineException(
ctx.handler().getClass().getName() +
".handlerAdded() has thrown an exception; removed.", t));
} else {
fireExceptionCaught(new ChannelPipelineException(
ctx.handler().getClass().getName() +
".handlerAdded() has thrown an exception; also failed to remove.", t));
}
}
}
作用:
调用 ctx-B 的 callHandlerAdded() 方法,并处理可能出现的异常。
步骤6:调用 ctx-B.callHandlerAdded()
final void callHandlerAdded() throws Exception {
// 设置状态为 ADD_COMPLETE,确保 handlerAdded 只被调用一次
if (setAddComplete()) {
// 这里的 handler() 返回的是 ctx-B 持有的 handler
// 也就是 MyChannelInitializer
handler().handlerAdded(this);
}
}
关键理解:
- 这里的
handler()返回的是 ctx-B 持有的 Handler - ctx-B 持有的是 MyChannelInitializer
- 所以这里调用的是 MyChannelInitializer 的
handlerAdded()方法
步骤7:调用 MyChannelInitializer 的 handlerAdded()
// ChannelInitializer.handlerAdded()
@Override
public void handlerAdded(ChannelHandlerContext ctx) throws Exception {
if (ctx.channel().isRegistered()) {
// 调用 initChannel(ctx)
if (initChannel(ctx)) {
removeState(ctx);
}
}
}
这个方法和 Initializer-A 的 handlerAdded() 是同一个方法(都是 ChannelInitializer 类的方法)。
步骤8:调用 initChannel(ChannelHandlerContext)
private boolean initChannel(ChannelHandlerContext ctx) throws Exception {
if (initMap.add(ctx)) {
try {
// 调用抽象方法 initChannel(Channel)
// 由于多态,这里会调用 MyChannelInitializer 重写的版本
initChannel((C) ctx.channel());
} catch (Throwable cause) {
exceptionCaught(ctx, cause);
} finally {
// 将 MyChannelInitializer 从 Pipeline 中移除
ChannelPipeline pipeline = ctx.pipeline();
if (pipeline.context(this) != null) {
pipeline.remove(this);
}
}
return true;
}
return false;
}
步骤9:调用 MyChannelInitializer 重写的 initChannel(Channel)
// 这是我们自定义的 MyChannelInitializer
public class MyChannelInitializer extends ChannelInitializer<SocketChannel> {
@Override
protected void initChannel(SocketChannel channel) throws Exception {
// 这里是我们的业务逻辑
System.out.println("链接报告开始");
System.out.println("链接报告信息:本客户端链接到服务端。channelId:" + channel.id());
System.out.println("链接报告完毕");
// 我们可以在这里添加其他 Handler
// channel.pipeline().addLast(new StringDecoder());
// channel.pipeline().addLast(new StringEncoder());
// channel.pipeline().addLast(new MyBusinessHandler());
}
}
作用:
这就是我们自定义的初始化逻辑!在这里,我们可以:
- 打印日志
- 添加编解码器
- 添加业务 Handler
- 进行其他初始化操作
执行完成后,MyChannelInitializer 也会在 finally 块中被移除。
完整流程图
让我们用一张图来总结整个调用流程:
时间线:T1 - 客户端启动
|
v
b.handler(new MyChannelInitializer())
[MyChannelInitializer 实例被保存在 config 中]
-------------------------------------------------------------------
时间线:T2 - 调用 b.connect()
|
v
Bootstrap.init(channel)
|
v
创建 Initializer-A(匿名 ChannelInitializer)
|
v
p.addLast(Initializer-A)
|
v
创建 ctx-A,持有 Initializer-A 的引用
|
v
将 ctx-A 添加到 Pipeline
|
v
创建 PendingHandlerAddedTask(ctx-A)
Pipeline 状态:head <-> ctx-A(Initializer-A) <-> tail
-------------------------------------------------------------------
时间线:T3 - Channel 注册完成
|
v
执行 PendingHandlerAddedTask
|
v
callHandlerAdded0(ctx-A)
|
v
ctx-A.callHandlerAdded()
|
v
ctx-A.handler().handlerAdded(ctx-A)
[这里的 handler() 返回 Initializer-A]
|
v
Initializer-A.handlerAdded(ctx-A)
|
v
Initializer-A.initChannel(ctx-A)
|
v
Initializer-A.initChannel(channel) ← 调用匿名内部类重写的方法
|
v
获取 config.handler() [返回 MyChannelInitializer]
|
v
pipeline.addLast(MyChannelInitializer)
|
v
Initializer-A 被移除
Pipeline 状态:head <-> ctx-B(MyChannelInitializer) <-> tail
-------------------------------------------------------------------
时间线:T4 - MyChannelInitializer 被添加
|
v
创建 ctx-B,持有 MyChannelInitializer 的引用
|
v
因为 registered = true,直接调用 callHandlerAdded0(ctx-B)
|
v
ctx-B.callHandlerAdded()
|
v
ctx-B.handler().handlerAdded(ctx-B)
[这里的 handler() 返回 MyChannelInitializer]
|
v
MyChannelInitializer.handlerAdded(ctx-B)
|
v
MyChannelInitializer.initChannel(ctx-B)
|
v
MyChannelInitializer.initChannel(channel) ← 调用我们重写的方法
|
v
执行我们的业务逻辑:
- 打印日志
- 添加其他 Handler
|
v
MyChannelInitializer 被移除
最终 Pipeline 状态:head <-> [我们添加的 Handler] <-> tail
核心要点总结
1. 两个不同的 ChannelInitializer 实例
-
Initializer-A:Bootstrap.init() 中创建的匿名内部类
- 作用:将用户设置的 Handler(MyChannelInitializer)添加到 Pipeline
- 生命周期:在 register 完成后被调用,执行完立即移除
-
MyChannelInitializer:用户自定义的 ChannelInitializer
- 作用:执行用户的初始化逻辑,添加业务 Handler
- 生命周期:被 Initializer-A 添加到 Pipeline 后立即被调用,执行完立即移除
2. 为什么 callHandlerAdded() 调用的是 Initializer-A?
因为:
- 在准备阶段,
p.addLast(Initializer-A)创建了 ctx-A - ctx-A 持有的 handler 引用指向 Initializer-A
- PendingHandlerAddedTask 持有的是 ctx-A 的引用
- 执行阶段调用
ctx-A.handler().handlerAdded()时,返回的是 Initializer-A
关键理解:Context 持有哪个 Handler 的引用,handler() 方法就返回哪个 Handler。
3. registered 标志的作用
registered 标志决定了 Handler 的初始化时机:
-
registered = false(Channel 还没注册)
- 创建 PendingHandlerAddedTask,延迟到注册完成后执行
- Initializer-A 就是这种情况
-
registered = true(Channel 已经注册)
- 直接调用
callHandlerAdded0(),立即执行初始化 - MyChannelInitializer 就是这种情况
- 直接调用
4. ChannelInitializer 的自动移除机制
ChannelInitializer 在 initChannel(ChannelHandlerContext) 方法的 finally 块中会自动移除自己:
finally {
ChannelPipeline pipeline = ctx.pipeline();
if (pipeline.context(this) != null) {
pipeline.remove(this);
}
}
这确保了 ChannelInitializer 只执行一次,执行完就从 Pipeline 中消失。
5. 多态机制的应用
整个流程中,Java 的多态机制起到了关键作用:
// ChannelInitializer 中的代码
private boolean initChannel(ChannelHandlerContext ctx) throws Exception {
// 调用抽象方法
initChannel((C) ctx.channel());
}
虽然这里调用的是抽象方法 initChannel(Channel),但由于多态:
- 如果当前实例是 Initializer-A,就调用 Initializer-A 重写的版本
- 如果当前实例是 MyChannelInitializer,就调用 MyChannelInitializer 重写的版本
最终总结
通过本文的详细分析,我们完整地理解了 Netty Pipeline 的初始化流程。让我们用最简洁的方式总结一下核心要点:
整个流程的本质
Pipeline 初始化实际上是一个两阶段、两层 ChannelInitializer 的过程:
阶段一:准备阶段(Init)
- 创建 Initializer-A(匿名 ChannelInitializer)
- 将 Initializer-A 添加到 Pipeline
- 创建 PendingHandlerAddedTask,等待执行
阶段二:执行阶段(Register)
-
第一层初始化:执行 Initializer-A 的
initChannel()- 作用:将 MyChannelInitializer 添加到 Pipeline
- 结果:Initializer-A 被移除,MyChannelInitializer 被添加
-
第二层初始化:执行 MyChannelInitializer 的
initChannel()- 作用:执行用户的业务逻辑,添加业务 Handler
- 结果:MyChannelInitializer 被移除,业务 Handler 留在 Pipeline
为什么要这样设计?
- 统一性:客户端和服务端使用相同的初始化流程
- 灵活性:通过 Initializer-A 作为中间层,可以在添加用户 Handler 前后做额外处理
- 安全性:延迟初始化确保在正确的时机(Channel 注册后)执行
- 清晰性:职责分离,Initializer-A 负责框架逻辑,MyChannelInitializer 负责业务逻辑
关键理解点
- Context 持有 Handler:
handler()方法返回的是 Context 持有的 Handler 引用 - 多态机制:调用抽象方法时,会根据实际实例类型调用对应的重写版本
- registered 标志:决定是延迟执行还是立即执行
- 自动移除:ChannelInitializer 执行完后会自动从 Pipeline 中移除
Pipeline 状态变化总结
初始状态:
head <-> tail
准备阶段后:
head <-> ctx-A(Initializer-A) <-> tail
[待处理任务队列:PendingHandlerAddedTask(ctx-A)]
第一层初始化后:
head <-> ctx-B(MyChannelInitializer) <-> tail
[Initializer-A 已移除]
第二层初始化后:
head <-> [业务 Handler] <-> tail
[MyChannelInitializer 已移除]
1920



被折叠的 条评论
为什么被折叠?



