ChannelPipeline 事件传播详解 - executionMask 机制
一、executionMask 是什么?
1.1 定义
executionMask 是 ChannelHandlerContext 的一个整型字段,用位掩码的方式记录当前 Handler 实现了哪些方法。
// AbstractChannelHandlerContext.java
abstract class AbstractChannelHandlerContext implements ChannelHandlerContext {
// 执行掩码:标识这个 Handler 实现了哪些方法
private final int executionMask;
AbstractChannelHandlerContext(DefaultChannelPipeline pipeline, EventExecutor executor,
String name, Class<? extends ChannelHandler> handlerClass) {
this.name = ObjectUtil.checkNotNull(name, "name");
this.pipeline = pipeline;
this.executor = executor;
// 计算执行掩码
this.executionMask = mask(handlerClass);
// ...
}
}
1.2 为什么需要 executionMask?
性能优化! 避免不必要的方法调用。
没有 executionMask 的情况:
// 每次事件传播都要调用 Handler 的方法
handler.channelRead(ctx, msg); // 即使 Handler 没有重写这个方法
有 executionMask 的情况:
// 先检查 Handler 是否实现了这个方法
if ((ctx.executionMask & MASK_CHANNEL_READ) != 0) {
handler.channelRead(ctx, msg); // 只有实现了才调用
} else {
// 直接跳过,继续传播
ctx.fireChannelRead(msg);
}
二、executionMask 的计算过程
2.1 掩码常量定义
// ChannelHandlerMask.java
public final class ChannelHandlerMask {
// 入站事件掩码
static final int MASK_EXCEPTION_CAUGHT = 1; // 0000 0001
static final int MASK_CHANNEL_REGISTERED = 1 << 1; // 0000 0010
static final int MASK_CHANNEL_UNREGISTERED = 1 << 2; // 0000 0100
static final int MASK_CHANNEL_ACTIVE = 1 << 3; // 0000 1000
static final int MASK_CHANNEL_INACTIVE = 1 << 4; // 0001 0000
static final int MASK_CHANNEL_READ = 1 << 5; // 0010 0000
static final int MASK_CHANNEL_READ_COMPLETE = 1 << 6; // 0100 0000
static final int MASK_USER_EVENT_TRIGGERED = 1 << 7; // 1000 0000
static final int MASK_CHANNEL_WRITABILITY_CHANGED = 1 << 8;
// 出站事件掩码
static final int MASK_BIND = 1 << 9;
static final int MASK_CONNECT = 1 << 10;
static final int MASK_DISCONNECT = 1 << 11;
static final int MASK_CLOSE = 1 << 12;
static final int MASK_DEREGISTER = 1 << 13;
static final int MASK_READ = 1 << 14;
static final int MASK_WRITE = 1 << 15;
static final int MASK_FLUSH = 1 << 16;
// 所有入站事件的掩码(OR 运算)
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 |
MASK_EXCEPTION_CAUGHT;
// 所有出站事件的掩码(OR 运算)
static final int MASK_ONLY_OUTBOUND =
MASK_BIND |
MASK_CONNECT |
MASK_DISCONNECT |
MASK_CLOSE |
MASK_DEREGISTER |
MASK_READ |
MASK_WRITE |
MASK_FLUSH;
}
2.2 计算 Handler 的 executionMask
当 Handler 被添加到 Pipeline 时,Netty 会扫描 Handler 类,检查它实现了哪些方法:
// ChannelHandlerMask.java
static int mask(Class<? extends ChannelHandler> clazz) {
// 尝试从缓存中获取
Map<Class<? extends ChannelHandler>, Integer> cache = MASKS.get();
Integer mask = cache.get(clazz);
if (mask == null) {
// 计算掩码
mask = mask0(clazz);
cache.put(clazz, mask);
}
return mask;
}
private static int mask0(Class<? extends ChannelHandler> handlerType) {
int mask = MASK_EXCEPTION_CAUGHT; // 默认包含异常处理
try {
// 检查是否是 ChannelInboundHandler
if (ChannelInboundHandler.class.isAssignableFrom(handlerType)) {
mask |= MASK_ALL_INBOUND; // 先假设实现了所有入站方法
// 检查每个入站方法是否被重写
if (isSkippable(handlerType, "channelRegistered", ChannelHandlerContext.class)) {
mask &= ~MASK_CHANNEL_REGISTERED; // 没重写,清除对应位
}
if (isSkippable(handlerType, "channelUnregistered", ChannelHandlerContext.class)) {
mask &= ~MASK_CHANNEL_UNREGISTERED;
}
if (isSkippable(handlerType, "channelActive", ChannelHandlerContext.class)) {
mask &= ~MASK_CHANNEL_ACTIVE;
}
if (isSkippable(handlerType, "channelInactive", ChannelHandlerContext.class)) {
mask &= ~MASK_CHANNEL_INACTIVE;
}
if (isSkippable(handlerType, "channelRead", ChannelHandlerContext.class, Object.class)) {
mask &= ~MASK_CHANNEL_READ;
}
if (isSkippable(handlerType, "channelReadComplete", ChannelHandlerContext.class)) {
mask &= ~MASK_CHANNEL_READ_COMPLETE;
}
if (isSkippable(handlerType, "userEventTriggered", ChannelHandlerContext.class, Object.class)) {
mask &= ~MASK_USER_EVENT_TRIGGERED;
}
if (isSkippable(handlerType, "channelWritabilityChanged", ChannelHandlerContext.class)) {
mask &= ~MASK_CHANNEL_WRITABILITY_CHANGED;
}
}
// 检查是否是 ChannelOutboundHandler
if (ChannelOutboundHandler.class.isAssignableFrom(handlerType)) {
// 类似的检查出站方法...
}
} catch (Exception e) {
// 异常时返回所有掩码
return MASK_ALL_INBOUND | MASK_ALL_OUTBOUND;
}
return mask;
}
// 检查方法是否可以跳过(没有被重写)
private static boolean isSkippable(
final Class<?> handlerType,
final String methodName,
final Class<?>... paramTypes) throws Exception {
return AccessController.doPrivileged(new PrivilegedExceptionAction<Boolean>() {
@Override
public Boolean run() throws Exception {
Method m = handlerType.getMethod(methodName, paramTypes);
// 检查方法是否被 @Skip 注解标记,或者是否在基类中定义
return m.isAnnotationPresent(Skip.class) ||
m.getDeclaringClass().equals(ChannelInboundHandlerAdapter.class) ||
m.getDeclaringClass().equals(ChannelOutboundHandlerAdapter.class) ||
m.getDeclaringClass().equals(ChannelDuplexHandler.class);
}
});
}
2.3 实际示例
示例 1:只实现了 channelRead
public class MyHandler extends ChannelInboundHandlerAdapter {
@Override
public void channelRead(ChannelHandlerContext ctx, Object msg) {
System.out.println("收到消息: " + msg);
ctx.fireChannelRead(msg);
}
// 其他方法都没有重写
}
计算 executionMask:
初始值:MASK_EXCEPTION_CAUGHT = 0000 0001
检查 ChannelInboundHandler:
先设置所有入站位:mask |= MASK_ALL_INBOUND
检查 channelRegistered:没重写,清除该位
检查 channelUnregistered:没重写,清除该位
检查 channelActive:没重写,清除该位
检查 channelInactive:没重写,清除该位
检查 channelRead:重写了,保留该位 ✓
检查 channelReadComplete:没重写,清除该位
检查 userEventTriggered:没重写,清除该位
检查 channelWritabilityChanged:没重写,清除该位
最终 executionMask = MASK_EXCEPTION_CAUGHT | MASK_CHANNEL_READ
= 0000 0001 | 0010 0000
= 0010 0001
示例 2:实现了多个方法
public class MyHandler extends ChannelInboundHandlerAdapter {
@Override
public void channelActive(ChannelHandlerContext ctx) {
System.out.println("连接激活");
ctx.fireChannelActive();
}
@Override
public void channelRead(ChannelHandlerContext ctx, Object msg) {
System.out.println("收到消息: " + msg);
ctx.fireChannelRead(msg);
}
@Override
public void channelInactive(ChannelHandlerContext ctx) {
System.out.println("连接断开");
ctx.fireChannelInactive();
}
}
计算 executionMask:
最终 executionMask = MASK_EXCEPTION_CAUGHT |
MASK_CHANNEL_ACTIVE |
MASK_CHANNEL_READ |
MASK_CHANNEL_INACTIVE
= 0000 0001 | 0000 1000 | 0010 0000 | 0001 0000
= 0011 1001
三、事件传播的完整流程(详细版)
3.1 场景设置
假设 Pipeline 结构如下:
head <-> Handler1 <-> Handler2 <-> Handler3 <-> tail
Handler 实现:
// Handler1:只实现了 channelRead
class Handler1 extends ChannelInboundHandlerAdapter {
@Override
public void channelRead(ChannelHandlerContext ctx, Object msg) {
System.out.println("Handler1.channelRead");
ctx.fireChannelRead(msg);
}
}
// Handler2:实现了 channelActive 和 channelRead
class Handler2 extends ChannelInboundHandlerAdapter {
@Override
public void channelActive(ChannelHandlerContext ctx) {
System.out.println("Handler2.channelActive");
ctx.fireChannelActive();
}
@Override
public void channelRead(ChannelHandlerContext ctx, Object msg) {
System.out.println("Handler2.channelRead");
ctx.fireChannelRead(msg);
}
}
// Handler3:只实现了 channelActive
class Handler3 extends ChannelInboundHandlerAdapter {
@Override
public void channelActive(ChannelHandlerContext ctx) {
System.out.println("Handler3.channelActive");
ctx.fireChannelActive();
}
}
executionMask 值:
head.executionMask = 所有入站和出站掩码(特殊节点)
handler1.executionMask = MASK_EXCEPTION_CAUGHT | MASK_CHANNEL_READ
handler2.executionMask = MASK_EXCEPTION_CAUGHT | MASK_CHANNEL_ACTIVE | MASK_CHANNEL_READ
handler3.executionMask = MASK_EXCEPTION_CAUGHT | MASK_CHANNEL_ACTIVE
tail.executionMask = 所有入站掩码(特殊节点)
3.2 channelRead 事件传播流程
步骤 1:触发事件
pipeline.fireChannelRead(msg);
调用:
// DefaultChannelPipeline.java
public final ChannelPipeline fireChannelRead(Object msg) {
AbstractChannelHandlerContext.invokeChannelRead(head, msg);
return this;
}
步骤 2:head 节点处理
// AbstractChannelHandlerContext.java
static void invokeChannelRead(final AbstractChannelHandlerContext next, Object msg) {
EventExecutor executor = next.executor();
if (executor.inEventLoop()) {
// 在 EventLoop 线程中,直接调用
next.invokeChannelRead(msg);
} else {
// 不在 EventLoop 线程中,提交任务
executor.execute(() -> next.invokeChannelRead(msg));
}
}
调用:
// AbstractChannelHandlerContext.java
private void invokeChannelRead(Object msg) {
if (invokeHandler()) {
try {
// 调用 head 的 channelRead 方法
((ChannelInboundHandler) handler()).channelRead(this, msg);
} catch (Throwable t) {
invokeExceptionCaught(t);
}
} else {
fireChannelRead(msg);
}
}
head 的 channelRead 实现:
// HeadContext.java
@Override
public void channelRead(ChannelHandlerContext ctx, Object msg) {
// head 直接传播,不做处理
ctx.fireChannelRead(msg);
}
步骤 3:head 调用 ctx.fireChannelRead(msg)
// AbstractChannelHandlerContext.java
public ChannelHandlerContext fireChannelRead(final Object msg) {
// 查找下一个入站 Handler
invokeChannelRead(findContextInbound(MASK_CHANNEL_READ), msg);
return this;
}
关键:findContextInbound(MASK_CHANNEL_READ)
private AbstractChannelHandlerContext findContextInbound(int mask) {
AbstractChannelHandlerContext ctx = this; // ctx = head
EventExecutor currentExecutor = executor();
do {
ctx = ctx.next; // 第一次:ctx = handler1
} while (skipContext(ctx, currentExecutor, mask, MASK_ONLY_INBOUND));
return ctx;
}
检查 handler1 是否跳过:
private static boolean skipContext(
AbstractChannelHandlerContext ctx, // handler1
EventExecutor currentExecutor,
int mask, // MASK_CHANNEL_READ = 0010 0000
int onlyMask) { // MASK_ONLY_INBOUND
// 条件 1:检查是否是入站 Handler 且实现了 channelRead
boolean condition1 = (ctx.executionMask & (onlyMask | mask)) == 0;
// handler1.executionMask = 0010 0001 (MASK_EXCEPTION_CAUGHT | MASK_CHANNEL_READ)
// onlyMask | mask = MASK_ONLY_INBOUND | MASK_CHANNEL_READ
// (0010 0001 & (MASK_ONLY_INBOUND | 0010 0000)) != 0
// condition1 = false,不跳过
// 条件 2:检查 EventExecutor
boolean condition2 = (ctx.executor() == currentExecutor &&
(ctx.executionMask & mask) == 0);
// (handler1.executionMask & MASK_CHANNEL_READ) = 0010 0000 != 0
// condition2 = false
return condition1 || condition2; // false || false = false
}
结果: handler1 不跳过,返回 handler1 的 Context
步骤 4:handler1 处理
// 调用 handler1.channelRead()
invokeChannelRead(handler1Context, msg);
↓
handler1Context.invokeChannelRead(msg);
↓
((ChannelInboundHandler) handler()).channelRead(this, msg);
↓
Handler1.channelRead(ctx, msg) {
System.out.println("Handler1.channelRead"); // 输出
ctx.fireChannelRead(msg); // 继续传播
}
步骤 5:handler1 调用 ctx.fireChannelRead(msg)
再次调用 findContextInbound(MASK_CHANNEL_READ):
do {
ctx = ctx.next; // ctx = handler2
} while (skipContext(ctx, currentExecutor, mask, MASK_ONLY_INBOUND));
检查 handler2 是否跳过:
// handler2.executionMask = 0011 0001 (包含 MASK_CHANNEL_READ)
// (0011 0001 & MASK_CHANNEL_READ) = 0010 0000 != 0
// 不跳过
结果: handler2 不跳过,返回 handler2 的 Context
步骤 6:handler2 处理
Handler2.channelRead(ctx, msg) {
System.out.println("Handler2.channelRead"); // 输出
ctx.fireChannelRead(msg); // 继续传播
}
步骤 7:handler2 调用 ctx.fireChannelRead(msg)
再次调用 findContextInbound(MASK_CHANNEL_READ):
do {
ctx = ctx.next; // ctx = handler3
} while (skipContext(ctx, currentExecutor, mask, MASK_ONLY_INBOUND));
检查 handler3 是否跳过:
// handler3.executionMask = 0000 1001 (只有 MASK_EXCEPTION_CAUGHT | MASK_CHANNEL_ACTIVE)
// (0000 1001 & MASK_CHANNEL_READ) = 0000 0000 == 0
// 跳过!
结果: handler3 跳过,继续查找
do {
ctx = ctx.next; // ctx = tail
} while (skipContext(ctx, currentExecutor, mask, MASK_ONLY_INBOUND));
检查 tail 是否跳过:
// tail.executionMask = 所有入站掩码(包含 MASK_CHANNEL_READ)
// 不跳过
结果: 返回 tail 的 Context
步骤 8:tail 处理
TailContext.channelRead(ctx, msg) {
onUnhandledInboundMessage(ctx, msg);
// 释放资源
ReferenceCountUtil.release(msg);
}
3.3 完整流程图
pipeline.fireChannelRead(msg)
↓
invokeChannelRead(head, msg)
↓
head.invokeChannelRead(msg)
↓
head.channelRead(ctx, msg)
↓
ctx.fireChannelRead(msg)
↓
findContextInbound(MASK_CHANNEL_READ)
├─ 检查 handler1: executionMask & MASK_CHANNEL_READ != 0 ✓ 不跳过
└─ 返回 handler1Context
↓
invokeChannelRead(handler1Context, msg)
↓
Handler1.channelRead(ctx, msg) // 输出: Handler1.channelRead
↓
ctx.fireChannelRead(msg)
↓
findContextInbound(MASK_CHANNEL_READ)
├─ 检查 handler2: executionMask & MASK_CHANNEL_READ != 0 ✓ 不跳过
└─ 返回 handler2Context
↓
invokeChannelRead(handler2Context, msg)
↓
Handler2.channelRead(ctx, msg) // 输出: Handler2.channelRead
↓
ctx.fireChannelRead(msg)
↓
findContextInbound(MASK_CHANNEL_READ)
├─ 检查 handler3: executionMask & MASK_CHANNEL_READ == 0 ✗ 跳过
├─ 检查 tail: executionMask & MASK_CHANNEL_READ != 0 ✓ 不跳过
└─ 返回 tailContext
↓
invokeChannelRead(tailContext, msg)
↓
TailContext.channelRead(ctx, msg)
└─ 释放资源
四、channelActive 事件传播对比
使用相同的 Pipeline,触发 channelActive 事件:
pipeline.fireChannelActive();
传播流程
pipeline.fireChannelActive()
↓
invokeChannelActive(head)
↓
head.channelActive(ctx)
↓
ctx.fireChannelActive()
↓
findContextInbound(MASK_CHANNEL_ACTIVE)
├─ 检查 handler1: executionMask & MASK_CHANNEL_ACTIVE == 0 ✗ 跳过
├─ 检查 handler2: executionMask & MASK_CHANNEL_ACTIVE != 0 ✓ 不跳过
└─ 返回 handler2Context
↓
Handler2.channelActive(ctx) // 输出: Handler2.channelActive
↓
ctx.fireChannelActive()
↓
findContextInbound(MASK_CHANNEL_ACTIVE)
├─ 检查 handler3: executionMask & MASK_CHANNEL_ACTIVE != 0 ✓ 不跳过
└─ 返回 handler3Context
↓
Handler3.channelActive(ctx) // 输出: Handler3.channelActive
↓
ctx.fireChannelActive()
↓
tail.channelActive(ctx)
注意: handler1 被跳过了,因为它没有实现 channelActive 方法!
五、总结
5.1 executionMask 的作用
- 性能优化:避免调用 Handler 没有实现的方法
- 快速跳过:通过位运算快速判断是否需要调用
- 自动计算:Handler 添加时自动扫描并计算
5.2 事件传播的关键步骤
- 触发事件:
pipeline.fireXxx() - 从 head 开始:
invokeXxx(head, ...) - 调用 Handler:
handler().xxx(ctx, ...) - 继续传播:
ctx.fireXxx(...) - 查找下一个:
findContextInbound(MASK_XXX) - 检查掩码:
skipContext()通过位运算判断 - 循环执行:直到 tail 或事件被终止
5.3 位运算检查逻辑
// 检查 Handler 是否实现了某个方法
if ((ctx.executionMask & MASK_CHANNEL_READ) != 0) {
// 实现了,调用
handler.channelRead(ctx, msg);
} else {
// 没实现,跳过
ctx.fireChannelRead(msg);
}
5.4 记忆要点
- executionMask 是位掩码,记录 Handler 实现了哪些方法
- 每个方法对应一个位,通过位运算快速判断
- skipContext() 决定是否跳过 Handler
- 只有实现了方法的 Handler 才会被调用
- 事件传播是链式的,每个 Handler 调用
ctx.fireXxx()继续传播
200



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



