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

posted @ 2022-12-05 15:16  keep每天进步一点点  阅读(98)  评论(0)    收藏  举报