ChannelHandlerMask学习
前言
在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 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
1
结果正好是0,那这个handler就可以跳过了,while进入下一个循环,去寻找链表中的下一个(或上一个)handler。
总结
ChannelHandlerMask利用计算机中的位运算,通过或、与、非操作,进行0和1的变换从而进行筛选。利用好计算机的操作或、与、非可以极大的提高效率。
用一个数来表示一个handler,判断这个数就可以判断身份,用处。使用数[万物皆数]更方便。
————————————————
版权声明:本文为CSDN博主「HelloWorld_Von」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。
原文链接:https://blog.csdn.net/weixin_43671840/article/details/120811970

浙公网安备 33010602011771号