2024-04-04  阅读(2)
版权声明:本文为博主付费文章,严禁任何形式的转载和摘抄,维权必究。 本文链接:https://www.skjava.com/mianshi/baodian/detail/1649312796

回答

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;
    }