前言
在netty中有一个类ChannelHandlerMask,通过这个类可以计算出executionMask值,然后通过传入的mask进行(ctx.executionMask & mask) == 0 ,来判断是否是当前handler,从而找出下一个执行的handler。这也是netty中事件传播原理中的重要一环。
本文5795字,阅读并理解大约需要10分钟。天才忽略。
正文
首先需要知道的是
- executionMask在AbstractChannelHandlerContext中:
private final int executionMask;
- 寻找handler处:
private AbstractChannelHandlerContext findContextInbound(int mask) {
AbstractChannelHandlerContext ctx = this;
do {
ctx = ctx.next;
} while ((ctx.executionMask & mask) == 0);
return ctx;
}
private AbstractChannelHandlerContext findContextOutbound(int mask) {
AbstractChannelHandlerContext ctx = this;
do {
ctx = ctx.prev;
} while ((ctx.executionMask & mask) == 0);
return ctx;
}
注:不同版本中while里面的条件可能不同,但原理是相通的。
ChannelHandlerMask
成员变量
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值虽然多,但是不外乎3中,inbound、outbound和exception。netty的作者把inbound、outbound和exception每一个行为都用一位"1"表示了,总共17个1。之后所有的handler都逃不出这17个1。
1 1111 1111 1111 1111
这也是MASK_ALL_OUTBOUND的值,左边的8位表示outbound,紧接着的8位标识inbound,最右边的1标识exception。
计算mask值
一个handler的executionMask值就是17位的二进制数表示的。在ChannelHandlerMask中mask0方法就是计算executionMask值的方法。原生的方法看起来不够简洁,稍微调整了一下。
private static int mask0(Class<? extends ChannelHandler> handlerType) {
int mask = MASK_EXCEPTION_CAUGHT;
if (ChannelInboundHandler.class.isAssignableFrom(handlerType)) {
mask |= MASK_ALL_INBOUND;
if (isSkippable(handlerType, "channelRegistered", ChannelHandlerContext.class)) {
mask &= ~MASK_CHANNEL_REGISTERED;
}
//...
}
if (ChannelOutboundHandler.class.isAssignableFrom(handlerType)) {
mask |= MASK_ALL_OUTBOUND;
if (isSkippable(handlerType, "bind", ChannelHandlerContext.class,
SocketAddress.class, ChannelPromise.class)) {
mask &= ~MASK_BIND;
}
//...
}
if (isSkippable(handlerType, "exceptionCaught", ChannelHandlerContext.class, Throwable.class)) {
mask &= ~MASK_EXCEPTION_CAUGHT;
}
return mask;
}
从方法的入参Class<? extends ChannelHandler> handlerType处可以看出传入的是一个ChannelHandler类型的class。这个class用于判断inbound、outbound。
ChannelInboundHandler.class.isAssignableFrom(handlerType)
和
ChannelOutboundHandler.class.isAssignableFrom(handlerType)
isAssignableFrom可以理解为相当于instanceOf一样,只不过instanceOf关键字是实例用的,isAssignableFrom用于class。
mask0中的另一个if中isSkippable又是干什么的呢?
在netty中有一个注解Skip,netty通过反射可以判断一个class中method上是否有改注解。
isSkippable方法就是判断某个class中的某个方法是不是要跳过的。
当满足if条件后mask值是怎么变化的呢?
假如if条件满足,是一个inboundHandler,跳过registered方法,mask值计算就变成:
int mask = MASK_EXCEPTION_CAUGHT;
mask |= MASK_ALL_INBOUND;
mask &= ~MASK_CHANNEL_REGISTERED;
初始化:
mask:0 0000 0000 0000 0001
mask |= MASK_ALL_INBOUND
mask:0 0000 0001 1111 1111
也就是右边的9位是1。
mask &= ~MASK_CHANNEL_REGISTERED
~MASK_CHANNEL_REGISTERED:1 1111 1111 1111 1101
旧的mask: 0 0000 0001 1111 1111
&=~之后mask: 0 0000 0001 1111 1101
可以看到正好把registered所代表的这位变为0的。
这样mask值的样子我们就知道了。无非就是全部是1,全部是0或有些位是1或0的值。
极端的样子:
1 1111 1111 1111 1111
0 0000 0000 0000 0000
正常的样子:
1 0011 1111 0000 0001
1 1111 1111 1111 1101
等等
例子:
1.TailContext
TailContext是ChannelInboundHandler,它的executionMask计算一下应该是:
0 0000 0001 1111 1111
也就是1<<9 -1=512-1=511。
结果验证:
2.HeadContext
HeadContext是ChannelInboundHandler和ChannelOutboundHandler的合体,预计mask值是(1<<17)-1,也就是131071,二进制:1 1111 1111 1111 1111。
结果验证:
3.自定义handler
最后看一个有Skip注解的例子:
ChannelDuplexHandler:把inbound,outbound,异常方法通通都加上了Skip注解,预计mask值为0。为了能方便看到过程,写了个MyChannelDuplexHandler,继承自ChannelDuplexHandler,重写了channelRegistered方法。
预计它的executionMask值为2。
结果验证:
应用mask值
在有了这个executionMask值后就可以找handler了。
netty中找handler的地方,通过findContextInbound方法来寻找。
invokeChannelRegistered(findContextInbound(MASK_CHANNEL_REGISTERED));
可以看到方法中传入的mask也是ChannelHandlerMask中的值。例子中的MASK_CHANNEL_REGISTERED的值是:
0 0000 0000 0000 0010
如果一个handler是上面计算的executionMask值
0 0000 0001 1111 1101
两者进行下面的判断:
(ctx.executionMask & mask) == 0
结果正好是0,那这个handler就可以跳过了,while进入下一个循环,去寻找链表中的下一个(或上一个)handler。
总结
- ChannelHandlerMask利用计算机中的位运算,通过或、与、非操作,进行0和1的变换从而进行筛选。利用好计算机的操作或、与、非可以极大的提高效率。
- 用一个数来表示一个handler,判断这个数就可以判断身份,用处。使用数[万物皆数]更方便。