ChannelPipeline 入站事件处理机制详解
一、入站事件概述
1.1 什么是入站事件?
入站事件(Inbound Event)是指从网络底层向应用层传播的事件,代表数据从外部进入到应用程序的过程。
常见的入站事件包括:
channelRegistered()- Channel 注册到 EventLoopchannelActive()- Channel 激活(连接建立)channelRead()- 读取到数据channelReadComplete()- 数据读取完成channelInactive()- Channel 失活(连接断开)channelUnregistered()- Channel 从 EventLoop 注销exceptionCaught()- 捕获异常
1.2 入站事件的传播方向
入站事件从 head 节点开始,向 tail 节点传播
head -> Handler1(Inbound) -> Handler2(Inbound) -> Handler3(Inbound) -> tail
这与出站事件相反(出站事件从 tail 到 head)。
二、ChannelPipeline 的双向链表结构
2.1 Pipeline 的初始化
当创建 Channel 时,会自动创建一个 ChannelPipeline:
// AbstractChannel 构造器
protected AbstractChannel(Channel parent) {
this.parent = parent;
this.id = this.newId();
this.unsafe = this.newUnsafe();
this.pipeline = this.newChannelPipeline(); // 创建 Pipeline
}
protected DefaultChannelPipeline newChannelPipeline() {
return new DefaultChannelPipeline(this);
}
2.2 DefaultChannelPipeline 的构造
protected DefaultChannelPipeline(Channel channel) {
this.channel = ObjectUtil.checkNotNull(channel, "channel");
this.succeededFuture = new SucceededChannelFuture(channel, null);
this.voidPromise = new VoidChannelPromise(channel, true);
// 创建尾节点
this.tail = new TailContext(this);
// 创建头节点
this.head = new HeadContext(this);
// 构建双向链表:head <-> tail
this.head.next = this.tail;
this.tail.prev = this.head;
}
初始状态的 Pipeline 结构:
head <-> tail
2.3 添加 Handler 后的结构
当我们通过 pipeline.addLast(handler) 添加 Handler 后:
head <-> HandlerContext1 <-> HandlerContext2 <-> HandlerContext3 <-> tail
每个 Handler 都被包装在一个 ChannelHandlerContext 中。
三、入站事件的触发机制
3.1 入站事件触发的完整流程(以 OP_ACCEPT 为例)
入站事件由 Netty 底层触发,从网络事件检测到 Pipeline 事件传播的完整流程如下:
客户端发起连接
↓
操作系统接收连接请求
↓
Selector 检测到 OP_ACCEPT 事件
↓
NioEventLoop.run() 循环检测
↓
NioEventLoop.processSelectedKeys() 处理就绪事件
↓
NioEventLoop.processSelectedKey() 处理单个 Channel
↓
NioServerSocketChannel.NioServerSocketChannelUnsafe.read() 接受连接
↓
pipeline.fireChannelRead(socketChannel) 触发入站事件
↓
事件在 Pipeline 中传播(head → tail)
步骤 1:Channel 注册到 EventLoop
在服务端启动时,ServerSocketChannel 会注册到 bossGroup 的 EventLoop:
// AbstractChannel.AbstractUnsafe
public final void register(EventLoop eventLoop, final ChannelPromise promise) {
// ...
AbstractChannel.this.eventLoop = eventLoop;
if (eventLoop.inEventLoop()) {
register0(promise);
} else {
eventLoop.execute(new Runnable() {
@Override
public void run() {
register0(promise);
}
});
}
}
private void register0(ChannelPromise promise) {
// 调用 JDK 的 register 方法,注册到 Selector
doRegister();
// 触发 channelRegistered 事件
pipeline.fireChannelRegistered();
if (isActive()) {
// 触发 channelActive 事件
pipeline.fireChannelActive();
// 开始监听 OP_ACCEPT 事件
readIfIsAutoRead();
}
}
关键点: 注册完成后,Channel 开始监听 OP_ACCEPT 事件。
步骤 2:NioEventLoop 无限循环检测事件
EventLoop 的 run() 方法会不断循环,检测网络事件:
// NioEventLoop.java
protected void run() {
for (;;) { // 无限循环
try {
// 1. 计算选择策略
switch (selectStrategy.calculateStrategy(selectNowSupplier, hasTasks())) {
case SelectStrategy.CONTINUE:
continue;
case SelectStrategy.SELECT:
// 2. 阻塞等待事件(或定时等待)
select(wakenUp.getAndSet(false));
// 如果被唤醒,唤醒 Selector
if (wakenUp.get()) {
selector.wakeup();
}
default:
}
cancelledKeys = 0;
needsToSelectAgain = false;
final int ioRatio = this.ioRatio;
if (ioRatio == 100) {
try {
// 3. 处理所有就绪的 I/O 事件
processSelectedKeys();
} finally {
// 4. 执行任务队列中的任务
runAllTasks();
}
} else {
final long ioStartTime = System.nanoTime();
try {
// 3. 处理所有就绪的 I/O 事件
processSelectedKeys();
} finally {
final long ioTime = System.nanoTime() - ioStartTime;
// 4. 执行任务队列中的任务(按比例分配时间)
runAllTasks(ioTime * (100 - ioRatio) / ioRatio);
}
}
} catch (Throwable t) {
handleLoopException(t);
}
}
}
关键点:
select()方法会阻塞等待,直到有事件就绪或超时- 当客户端发起连接时,
Selector会检测到OP_ACCEPT事件 processSelectedKeys()会处理所有就绪的事件
步骤 3:处理所有就绪的 SelectionKey
// NioEventLoop.java
private void processSelectedKeys() {
if (selectedKeys != null) {
// 使用优化的 SelectedSelectionKeySet
processSelectedKeysOptimized();
} else {
// 使用 JDK 原生的 Set
processSelectedKeysPlain(selector.selectedKeys());
}
}
private void processSelectedKeysOptimized() {
for (int i = 0; i < selectedKeys.size; ++i) {
final SelectionKey k = selectedKeys.keys[i];
// 清空引用,帮助 GC
selectedKeys.keys[i] = null;
// 获取 SelectionKey 的 attachment(就是注册时附加的 Channel)
final Object a = k.attachment();
if (a instanceof AbstractNioChannel) {
// 处理这个 Channel 的事件
processSelectedKey(k, (AbstractNioChannel) a);
} else {
@SuppressWarnings("unchecked")
NioTask<SelectableChannel> task = (NioTask<SelectableChannel>) a;
processSelectedKey(k, task);
}
if (needsToSelectAgain) {
// 需要重新 select
selectedKeys.reset(i + 1);
selectAgain();
i = -1;
}
}
}
关键点:
SelectionKey的attachment就是NioServerSocketChannel- 遍历所有就绪的
SelectionKey,逐个处理
步骤 4:处理单个 Channel 的就绪事件
// NioEventLoop.java
private void processSelectedKey(SelectionKey k, AbstractNioChannel ch) {
// 获取 Channel 的 Unsafe 对象
final AbstractNioChannel.NioUnsafe unsafe = ch.unsafe();
// 检查 key 是否有效
if (!k.isValid()) {
final EventLoop eventLoop;
try {
eventLoop = ch.eventLoop();
} catch (Throwable ignored) {
return;
}
if (eventLoop == this) {
// key 无效,关闭 Channel
unsafe.close(unsafe.voidPromise());
}
return;
}
try {
// 获取就绪的操作类型
int readyOps = k.readyOps();
// 处理 OP_CONNECT 事件(客户端连接完成)
if ((readyOps & SelectionKey.OP_CONNECT) != 0) {
int ops = k.interestOps();
ops &= ~SelectionKey.OP_CONNECT;
k.interestOps(ops);
unsafe.finishConnect();
}
// 处理 OP_WRITE 事件(可写)
if ((readyOps & SelectionKey.OP_WRITE) != 0) {
ch.unsafe().forceFlush();
}
// 处理 OP_READ 或 OP_ACCEPT 事件
// 这是入站事件的关键触发点!
if ((readyOps & (SelectionKey.OP_READ | SelectionKey.OP_ACCEPT)) != 0 || readyOps == 0) {
// 调用 Unsafe 的 read 方法
// 对于 ServerSocketChannel,这会接受新连接
// 对于 SocketChannel,这会读取数据
unsafe.read();
}
} catch (CancelledKeyException ignored) {
unsafe.close(unsafe.voidPromise());
}
}
关键点:
- 通过位运算检查就绪的操作类型
- 当检测到
OP_ACCEPT时,调用unsafe.read() - 这里的
unsafe是NioServerSocketChannel.NioServerSocketChannelUnsafe
步骤 5:NioServerSocketChannelUnsafe.read() 接受新连接
// NioServerSocketChannel.NioServerSocketChannelUnsafe
public void read() {
assert eventLoop().inEventLoop();
final ChannelConfig config = config();
final ChannelPipeline pipeline = pipeline();
final RecvByteBufAllocator.Handle allocHandle = unsafe().recvBufAllocHandle();
allocHandle.reset(config);
boolean closed = false;
Throwable exception = null;
try {
try {
do {
// 接受新连接,返回接受的连接数
// doReadMessages 会调用 ServerSocketChannel.accept()
// 并将新的 SocketChannel 添加到 readBuf 中
int localRead = doReadMessages(readBuf);
if (localRead == 0) {
// 没有更多连接
break;
}
if (localRead < 0) {
// 连接关闭
closed = true;
break;
}
// 增加已读消息计数
allocHandle.incMessagesRead(localRead);
} while (allocHandle.continueReading());
} catch (Throwable t) {
exception = t;
}
int size = readBuf.size();
for (int i = 0; i < size; i ++) {
readPending = false;
// 关键:触发 channelRead 事件!
// readBuf.get(i) 是新接受的 NioSocketChannel
pipeline.fireChannelRead(readBuf.get(i));
}
readBuf.clear();
allocHandle.readComplete();
// 触发 channelReadComplete 事件
pipeline.fireChannelReadComplete();
if (exception != null) {
closed = closeOnReadError(exception);
// 触发 exceptionCaught 事件
pipeline.fireExceptionCaught(exception);
}
if (closed) {
inputShutdown = true;
if (isOpen()) {
close(voidPromise());
}
}
} finally {
if (!readPending && !config.isAutoRead()) {
removeReadOp();
}
}
}
关键点:
doReadMessages()调用 JDK 的ServerSocketChannel.accept()接受新连接- 每接受一个新连接,就创建一个
NioSocketChannel对象 - 调用
pipeline.fireChannelRead(socketChannel)触发入站事件 - 注意:这里传递的是
SocketChannel对象,不是ByteBuf!
步骤 6:doReadMessages() 接受连接的底层实现
// NioServerSocketChannel.java
protected int doReadMessages(List<Object> buf) throws Exception {
// 调用 JDK 的 accept 方法接受新连接
SocketChannel ch = SocketUtils.accept(javaChannel());
try {
if (ch != null) {
// 将新连接包装成 NioSocketChannel,添加到 buf 中
buf.add(new NioSocketChannel(this, ch));
return 1; // 返回接受的连接数
}
} catch (Throwable t) {
logger.warn("Failed to create a new channel from an accepted socket.", t);
try {
ch.close();
} catch (Throwable t2) {
logger.warn("Failed to close a socket.", t2);
}
}
return 0; // 没有接受到连接
}
关键点:
- 调用 JDK 原生的
ServerSocketChannel.accept()方法 - 将返回的
SocketChannel包装成 Netty 的NioSocketChannel - 返回接受的连接数(1 或 0)
步骤 7:pipeline.fireChannelRead() 触发入站事件
// DefaultChannelPipeline.java
public final ChannelPipeline fireChannelRead(Object msg) {
// 从 head 节点开始传播
AbstractChannelHandlerContext.invokeChannelRead(head, msg);
return this;
}
关键点:
- 入站事件从
head节点开始传播 msg参数是新接受的NioSocketChannel对象
3.2 完整的调用链路图
┌─────────────────────────────────────────────────────────────┐
│ 1. 客户端发起连接 │
│ Socket socket = new Socket("localhost", 8080); │
└─────────────────────────────────────────────────────────────┘
↓
┌─────────────────────────────────────────────────────────────┐
│ 2. 操作系统接收连接请求 │
│ TCP 三次握手完成,连接进入 accept 队列 │
└─────────────────────────────────────────────────────────────┘
↓
┌─────────────────────────────────────────────────────────────┐
│ 3. Selector 检测到 OP_ACCEPT 事件 │
│ selector.select() 返回,selectedKeys 包含就绪的 Channel │
└─────────────────────────────────────────────────────────────┘
↓
┌─────────────────────────────────────────────────────────────┐
│ 4. NioEventLoop.run() │
│ for (;;) { │
│ select(wakenUp.getAndSet(false)); // 阻塞等待事件 │
│ processSelectedKeys(); // 处理就绪事件 │
│ runAllTasks(); // 执行任务队列 │
│ } │
└─────────────────────────────────────────────────────────────┘
↓
┌─────────────────────────────────────────────────────────────┐
│ 5. NioEventLoop.processSelectedKeys() │
│ 遍历所有就绪的 SelectionKey │
│ for (SelectionKey k : selectedKeys) { │
│ processSelectedKey(k, channel); │
│ } │
└─────────────────────────────────────────────────────────────┘
↓
┌─────────────────────────────────────────────────────────────┐
│ 6. NioEventLoop.processSelectedKey(k, serverSocketChannel) │
│ int readyOps = k.readyOps(); │
│ if ((readyOps & OP_ACCEPT) != 0) { │
│ unsafe.read(); // 调用 Unsafe 的 read 方法 │
│ } │
└─────────────────────────────────────────────────────────────┘
↓
┌─────────────────────────────────────────────────────────────┐
│ 7. NioServerSocketChannelUnsafe.read() │
│ do { │
│ int n = doReadMessages(readBuf); // 接受新连接 │
│ } while (allocHandle.continueReading()); │
│ │
│ for (Object msg : readBuf) { │
│ pipeline.fireChannelRead(msg); // 触发入站事件 │
│ } │
└─────────────────────────────────────────────────────────────┘
↓
┌─────────────────────────────────────────────────────────────┐
│ 8. NioServerSocketChannel.doReadMessages(readBuf) │
│ SocketChannel ch = serverSocketChannel.accept(); │
│ NioSocketChannel nioSocketChannel = new NioSocketChannel(this, ch); │
│ readBuf.add(nioSocketChannel); │
│ return 1; // 返回接受的连接数 │
└─────────────────────────────────────────────────────────────┘
↓
┌─────────────────────────────────────────────────────────────┐
│ 9. DefaultChannelPipeline.fireChannelRead(socketChannel) │
│ AbstractChannelHandlerContext.invokeChannelRead(head, msg);│
└─────────────────────────────────────────────────────────────┘
↓
┌─────────────────────────────────────────────────────────────┐
│ 10. 事件在 Pipeline 中传播 │
│ head -> ServerBootstrapAcceptor -> tail │
│ │
│ ServerBootstrapAcceptor 会将新连接注册到 workerGroup │
└─────────────────────────────────────────────────────────────┘
3.3 关键代码位置总结
| 步骤 | 类 | 方法 | 作用 |
|---|---|---|---|
| 1 | NioEventLoop | run() | 事件循环,不断检测事件 |
| 2 | NioEventLoop | select() | 阻塞等待事件就绪 |
| 3 | NioEventLoop | processSelectedKeys() | 处理所有就绪的事件 |
| 4 | NioEventLoop | processSelectedKey() | 处理单个 Channel 的事件 |
| 5 | NioServerSocketChannelUnsafe | read() | 接受新连接 |
| 6 | NioServerSocketChannel | doReadMessages() | 调用 JDK accept() |
| 7 | DefaultChannelPipeline | fireChannelRead() | 触发入站事件 |
| 8 | AbstractChannelHandlerContext | invokeChannelRead() | 开始事件传播 |
3.4 HeadContext 的特殊性
HeadContext 既实现了 ChannelInboundHandler,也实现了 ChannelOutboundHandler:
final class HeadContext extends AbstractChannelHandlerContext
implements ChannelOutboundHandler, ChannelInboundHandler {
private final Unsafe unsafe;
HeadContext(DefaultChannelPipeline pipeline) {
super(pipeline, null, HEAD_NAME, HeadContext.class);
this.unsafe = pipeline.channel().unsafe();
this.setAddComplete();
}
@Override
public void channelRead(ChannelHandlerContext ctx, Object msg) {
// HeadContext 的 channelRead 直接传递给下一个 Handler
ctx.fireChannelRead(msg);
}
// ... 其他方法
}
HeadContext 作为入站事件的起点,它的职责是:
- 接收底层传来的事件
- 立即将事件传递给下一个入站 Handler
3.5 时间线示例
假设服务端监听 8080 端口,客户端发起连接:
T0: 服务端启动,ServerSocketChannel 注册到 bossGroup
└─ 开始监听 OP_ACCEPT 事件
T1: NioEventLoop.run() 开始循环
└─ select() 阻塞等待
T2: 客户端执行 new Socket("localhost", 8080)
└─ 发起 TCP 连接请求
T3: TCP 三次握手完成
└─ 连接进入操作系统的 accept 队列
T4: Selector 检测到 OP_ACCEPT 事件
└─ select() 返回
T5: processSelectedKeys() 开始处理
└─ 遍历 selectedKeys
T6: processSelectedKey(k, serverSocketChannel)
└─ 检测到 readyOps & OP_ACCEPT != 0
T7: unsafe.read() 被调用
└─ NioServerSocketChannelUnsafe.read()
T8: doReadMessages(readBuf)
└─ serverSocketChannel.accept() 返回新的 SocketChannel
T9: new NioSocketChannel(this, ch)
└─ 包装成 Netty 的 Channel
T10: pipeline.fireChannelRead(socketChannel)
└─ 触发入站事件
T11: HeadContext.channelRead()
└─ ctx.fireChannelRead(msg)
T12: ServerBootstrapAcceptor.channelRead()
└─ 将新连接注册到 workerGroup
T13: 新连接开始监听 OP_READ 事件
└─ 等待客户端发送数据
四、入站事件的传播过程(核心)
4.1 什么是 executionMask(执行掩码)?
在深入事件传播之前,先理解 executionMask 的概念:
executionMask 的定义
executionMask 是 ChannelHandlerContext 的一个整型字段,用位掩码的方式记录当前 Handler 实现了哪些方法。
// AbstractChannelHandlerContext.java
abstract class AbstractChannelHandlerContext implements ChannelHandlerContext {
// 执行掩码:标识这个 Handler 实现了
步骤 2:调用静态方法 invokeChannelRead
// AbstractChannelHandlerContext.java
static void invokeChannelRead(final AbstractChannelHandlerContext next, Object msg) {
final Object m = next.pipeline.touch(ObjectUtil.checkNotNull(msg, "msg"), next);
EventExecutor executor = next.executor();
// 判断是否在 EventLoop 线程中
if (executor.inEventLoop()) {
// 在 EventLoop 线程中,直接调用
next.invokeChannelRead(m);
} else {
// 不在 EventLoop 线程中,提交任务到 EventLoop
executor.execute(new Runnable() {
@Override
public void run() {
next.invokeChannelRead(m);
}
});
}
}
关键点:确保事件处理在正确的线程中执行
步骤 3:调用实例方法 invokeChannelRead
// AbstractChannelHandlerContext.java
private void invokeChannelRead(Object msg) {
if (invokeHandler()) {
try {
// 调用当前 Handler 的 channelRead 方法
((ChannelInboundHandler) handler()).channelRead(this, msg);
} catch (Throwable t) {
invokeExceptionCaught(t);
}
} else {
// Handler 还未添加完成,继续传播
fireChannelRead(msg);
}
}
关键点:这里调用了实际的 Handler 的 channelRead 方法
步骤 4:用户 Handler 处理并传播
// 用户自定义的 Handler
public class MyInboundHandler extends ChannelInboundHandlerAdapter {
@Override
public void channelRead(ChannelHandlerContext ctx, Object msg) {
// 处理业务逻辑
System.out.println("收到消息: " + msg);
// 继续传播给下一个 Handler
ctx.fireChannelRead(msg);
}
}
步骤 5:查找下一个入站 Handler
// AbstractChannelHandlerContext.java
public ChannelHandlerContext fireChannelRead(final Object msg) {
// 查找下一个入站 Handler
invokeChannelRead(findContextInbound(MASK_CHANNEL_READ), msg);
return this;
}
这是最关键的部分! findContextInbound() 方法负责查找下一个入站 Handler。
4.2 查找下一个入站 Handler 的核心逻辑
// AbstractChannelHandlerContext.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;
}
关键方法:skipContext
private static boolean skipContext(
AbstractChannelHandlerContext ctx,
EventExecutor currentExecutor,
int mask,
int onlyMask) {
// 跳过的条件:
// 1. Handler 的执行掩码不匹配(不是我们要找的事件类型)
// 2. Handler 不是入站类型(onlyMask 检查)
return (ctx.executionMask & (onlyMask | mask)) == 0 ||
(ctx.executor() == currentExecutor && (ctx.executionMask & mask) == 0);
}
执行掩码(Execution Mask)机制
Netty 使用位掩码来标识 Handler 实现了哪些方法:
// ChannelHandlerMask.java
static final int MASK_EXCEPTION_CAUGHT = 1;
static final int MASK_CHANNEL_REGISTERED = 1 << 1;
static final int MASK_CHANNEL_UNREGISTERED = 1 << 2;
static final int MASK_CHANNEL_ACTIVE = 1 << 3;
static final int MASK_CHANNEL_INACTIVE = 1 << 4;
static final int MASK_CHANNEL_READ = 1 << 5;
static final int MASK_CHANNEL_READ_COMPLETE = 1 << 6;
// ... 更多掩码
static final int MASK_ONLY_INBOUND =
MASK_CHANNEL_REGISTERED |
MASK_CHANNEL_UNREGISTERED |
MASK_CHANNEL_ACTIVE |
MASK_CHANNEL_INACTIVE |
MASK_CHANNEL_READ |
MASK_CHANNEL_READ_COMPLETE |
MASK_USER_EVENT_TRIGGERED |
MASK_CHANNEL_WRITABILITY_CHANGED;
工作原理:
- 当 Handler 被添加到 Pipeline 时,Netty 会扫描 Handler 类,检查它实现了哪些方法
- 为每个实现的方法设置对应的位标志
- 在事件传播时,通过位运算快速判断 Handler 是否需要处理该事件
示例:
public class MyHandler extends ChannelInboundHandlerAdapter {
@Override
public void channelRead(ChannelHandlerContext ctx, Object msg) {
// 实现了 channelRead
}
// 没有实现 channelActive
}
这个 Handler 的执行掩码会包含 MASK_CHANNEL_READ,但不包含 MASK_CHANNEL_ACTIVE。
当 channelActive 事件传播时,findContextInbound() 会跳过这个 Handler,因为:
(ctx.executionMask & MASK_CHANNEL_ACTIVE) == 0 // 返回 true,跳过
五、完整的入站事件传播示例
5.1 示例代码
// 添加多个 Handler
pipeline.addLast("handler1", new MyInboundHandler1());
pipeline.addLast("handler2", new MyInboundHandler2());
pipeline.addLast("handler3", new MyInboundHandler3());
// Handler 实现
class MyInboundHandler1 extends ChannelInboundHandlerAdapter {
@Override
public void channelRead(ChannelHandlerContext ctx, Object msg) {
System.out.println("Handler1 处理消息");
ctx.fireChannelRead(msg); // 传播给下一个
}
}
class MyInboundHandler2 extends ChannelInboundHandlerAdapter {
@Override
public void channelRead(ChannelHandlerContext ctx, Object msg) {
System.out.println("Handler2 处理消息");
ctx.fireChannelRead(msg); // 传播给下一个
}
}
class MyInboundHandler3 extends ChannelInboundHandlerAdapter {
@Override
public void channelRead(ChannelHandlerContext ctx, Object msg) {
System.out.println("Handler3 处理消息");
// 不再传播,事件在这里终止
}
}
5.2 Pipeline 结构
head <-> handler1 <-> handler2 <-> handler3 <-> tail
5.3 事件传播流程
当底层读取到数据时:
1. pipeline.fireChannelRead(msg)
↓
2. head.channelRead(ctx, msg)
↓ ctx.fireChannelRead(msg)
3. handler1.channelRead(ctx, msg) // 输出: Handler1 处理消息
↓ ctx.fireChannelRead(msg)
4. handler2.channelRead(ctx, msg) // 输出: Handler2 处理消息
↓ ctx.fireChannelRead(msg)
5. handler3.channelRead(ctx, msg) // 输出: Handler3 处理消息
↓ 不再传播,事件结束
5.4 如果 Handler3 继续传播
如果 Handler3 也调用了 ctx.fireChannelRead(msg):
5. handler3.channelRead(ctx, msg)
↓ ctx.fireChannelRead(msg)
6. tail.channelRead(ctx, msg) // 到达 tail 节点
TailContext 的处理:
final class TailContext extends AbstractChannelHandlerContext
implements ChannelInboundHandler {
@Override
public void channelRead(ChannelHandlerContext ctx, Object msg) {
// 释放消息(如果是 ByteBuf)
onUnhandledInboundMessage(ctx, msg);
}
protected void onUnhandledInboundMessage(ChannelHandlerContext ctx, Object msg) {
try {
logger.debug(
"Discarded inbound message {} that reached at the tail of the pipeline. " +
"Please check your pipeline configuration.", msg);
} finally {
// 释放资源
ReferenceCountUtil.release(msg);
}
}
}
重要: TailContext 会释放未处理的消息,防止内存泄漏!
六、入站事件与出站事件的对比
6.1 传播方向
| 事件类型 | 起点 | 终点 | 传播方向 |
|---|---|---|---|
| 入站事件 | head | tail | head → tail |
| 出站事件 | tail | head | tail → head |
6.2 Handler 类型
| Handler 类型 | 处理的事件 | 典型方法 |
|---|---|---|
| ChannelInboundHandler | 入站事件 | channelRead, channelActive |
| ChannelOutboundHandler | 出站事件 | write, flush, connect |
6.3 查找下一个 Handler 的方法
// 入站:向后查找(next)
private AbstractChannelHandlerContext findContextInbound(int mask) {
AbstractChannelHandlerContext ctx = this;
do {
ctx = ctx.next; // 向后
} while (skipContext(ctx, currentExecutor, mask, MASK_ONLY_INBOUND));
return ctx;
}
// 出站:向前查找(prev)
private AbstractChannelHandlerContext findContextOutbound(int mask) {
AbstractChannelHandlerContext ctx = this;
do {
ctx = ctx.prev; // 向前
} while (skipContext(ctx, currentExecutor, mask, MASK_ONLY_OUTBOUND));
return ctx;
}
七、常见问题与最佳实践
7.1 为什么我的 Handler 没有被调用?
原因 1:忘记调用 ctx.fireChannelRead(msg)
// 错误示例
public void channelRead(ChannelHandlerContext ctx, Object msg) {
System.out.println("处理消息");
// 忘记传播,后续 Handler 不会被调用
}
// 正确示例
public void channelRead(ChannelHandlerContext ctx, Object msg) {
System.out.println("处理消息");
ctx.fireChannelRead(msg); // 必须传播
}
原因 2:Handler 类型不匹配
// 错误:使用 ChannelOutboundHandler 处理入站事件
public class MyHandler extends ChannelOutboundHandlerAdapter {
@Override
public void channelRead(ChannelHandlerContext ctx, Object msg) {
// 这个方法永远不会被调用!
// 因为 ChannelOutboundHandler 不处理入站事件
}
}
// 正确:使用 ChannelInboundHandler
public class MyHandler extends ChannelInboundHandlerAdapter {
@Override
public void channelRead(ChannelHandlerContext ctx, Object msg) {
// 正确
}
}
7.2 内存泄漏问题
问题: 如果不传播消息,也不释放,会导致内存泄漏。
// 危险示例
public void channelRead(ChannelHandlerContext ctx, Object msg) {
// 既不传播,也不释放
// 内存泄漏!
}
// 正确示例 1:传播
public void channelRead(ChannelHandlerContext ctx, Object msg) {
ctx.fireChannelRead(msg); // 传播给下一个 Handler
}
// 正确示例 2:手动释放
public void channelRead(ChannelHandlerContext ctx, Object msg) {
try {
// 处理消息
} finally {
ReferenceCountUtil.release(msg); // 释放资源
}
}
7.3 ctx.fireXxx() vs ctx.channel().pipeline().fireXxx()
// 方式 1:从当前 Handler 的下一个开始传播
ctx.fireChannelRead(msg);
// 方式 2:从 head 重新开始传播
ctx.channel().pipeline().fireChannelRead(msg);
区别:
ctx.fireXxx()- 从当前 Handler 的下一个开始pipeline().fireXxx()- 从 head 重新开始
性能建议: 优先使用 ctx.fireXxx(),避免重复处理。
7.4 异常处理
public class MyHandler extends ChannelInboundHandlerAdapter {
@Override
public void channelRead(ChannelHandlerContext ctx, Object msg) {
try {
// 处理消息
} catch (Exception e) {
// 触发异常事件
ctx.fireExceptionCaught(e);
}
}
@Override
public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) {
// 处理异常
cause.printStackTrace();
ctx.close();
}
}
八、总结
8.1 入站事件处理的核心要点
- 传播方向:head → tail
- 起点:
pipeline.fireXxx()从 head 开始 - 查找机制:通过
findContextInbound()查找下一个入站 Handler - 执行掩码:使用位运算快速判断 Handler 是否需要处理事件
- 线程安全:确保事件在正确的 EventLoop 线程中执行
- 资源管理:必须传播或释放消息,防止内存泄漏
8.2 关键方法调用链
pipeline.fireChannelRead(msg)
↓
AbstractChannelHandlerContext.invokeChannelRead(head, msg)
↓
head.invokeChannelRead(msg)
↓
((ChannelInboundHandler) handler()).channelRead(this, msg)
↓
ctx.fireChannelRead(msg)
↓
findContextInbound(MASK_CHANNEL_READ) // 查找下一个
↓
next.invokeChannelRead(msg)
↓
... 循环直到 tail 或事件被终止
3043



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



