回答
ChannelPipeline 是由一系列的 ChannelHandlerContext 实例组成的双向链表,每个 ChannelHandlerContext 实例包装了一个 ChannelHandler。当事件 ChannelPipeline 传播时,它实际上是通过 ChannelHandlerContext 的双向链表结构进行的。每个 ChannelHandlerContext 都有指向链表中前一个和后一个上下文的引用,这使得事件能够按顺序在管道中的处理器之间传递。
虽然,在 ChannelHandlerContext 可以获取 ChannelHandler,但是你怎么知道该 ChannelHandler 可以执行 channelRead()
呢?难道通过反射去判断它是否有 channelRead()
?虽然可行,但效率太低,Netty 采取了一种简单而又高效的方式。
在 Netty 中有一个类 ChannelHandlerMask,每一个 ChannelHandlerContext 节点都有一个 executionMask 值,在构建 ChannelPipeline 链表新建 ChannelHandlerContext 的时候会通过 ChannelHandlerMask 计算出它的 executionMask 值,然后在 findContextXxx()
的时候根据传入的 mask 与当前 ChannelHandler 进行计算,判断当前 ChannelHandler 是否可执行当前事件,从而找出下一个可执行的 ChannelHandler。
扩展
ChannelHandlerMask
Netty 对所有事件都定义了 mask 值:
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_USER_EVENT_TRIGGERED = 1 << 7;
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;
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;
private static final int MASK_ALL_INBOUND = MASK_EXCEPTION_CAUGHT | MASK_ONLY_INBOUND;
static final int MASK_ONLY_OUTBOUND = MASK_BIND | MASK_CONNECT | MASK_DISCONNECT |
MASK_CLOSE | MASK_DEREGISTER | MASK_READ | MASK_WRITE | MASK_FLUSH;
private static final int MASK_ALL_OUTBOUND = MASK_EXCEPTION_CAUGHT | MASK_ONLY_OUTBOUND;
从这些成员变量的命名我们就清楚知道每个 mask 值对应哪个事件。从这些值的定义可以看出,Netty 将所有 inbound、outbound 和 Exception 的每一个事件都用一位 "1" 表示,一共 17 位。
计算 mask 值
在构建 ChannelPipeline 双向链表的时候,我们都会将 ChannelHandler 包装到一个 ChannelHandlerContext 中,在 Netty 中,每一个 ChannelHandlerContext 都会有一个 mask 值与之对应,即 executionMask(定义在 AbstractChannelHandlerContext 中),在我们新建 ChannelHandlerContext 的时候会计算该值:
AbstractChannelHandlerContext(DefaultChannelPipeline pipeline, EventExecutor executor,
String name, Class<? extends ChannelHandler> handlerClass) {
// ...
this.executionMask = mask(handlerClass);
}
利用 ChannelHandler 的 Class调用 mask()
计算 executionMask:
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;
}