ChannelHandlerMask学习

本文详细介绍了Netty中的ChannelHandlerMask类,用于计算executionMask值,通过位运算判断handler执行顺序。内容包括成员变量解析,mask值计算方法及应用,揭示了Netty事件传播的原理。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

前言

在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。
结果验证:
MyChannelDuplexHandler的executionMask值验证

应用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,判断这个数就可以判断身份,用处。使用数[万物皆数]更方便。
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值