Netty源码_AbstractChannelHandlerContext详解

上下文ChannelHandlerContext 的最大作用就是向它所属管道ChannelPipeline 的上游或下游传递事件。
那么它是如何实现的呢?

这就要看 ChannelHandlerContext 接口的实现类 AbstractChannelHandlerContext

一. 成员属性

1.1 双向链表

    // 组成双向链表
    volatile AbstractChannelHandlerContext next;
    volatile AbstractChannelHandlerContext prev;

通过 nextprev 组成一个双向链表,这样就可以向上或者向下查找管道中的其他处理器上下文。

1.2 上下文状态

    /**
     * ChannelHandler.handlerAdded(ChannelHandlerContext) 即将被调用。
     */
    private static final int ADD_PENDING = 1;
    /**
     * ChannelHandler.handlerAdded(ChannelHandlerContext) 已经被调用。
     */
    private static final int ADD_COMPLETE = 2;
    /**
     * ChannelHandler.handlerRemoved(ChannelHandlerContext) 已经被调用。
     */
    private static final int REMOVE_COMPLETE = 3;
    /**
     * 初始状态,
     * ChannelHandler.handlerAdded(ChannelHandlerContext) 和
     * ChannelHandler.handlerRemoved(ChannelHandlerContext) 都没有被调用。
     *
     */
    private static final int INIT = 0;

    private volatile int handlerState = INIT;

    private static final AtomicIntegerFieldUpdater<AbstractChannelHandlerContext> HANDLER_STATE_UPDATER 
         = AtomicIntegerFieldUpdater.newUpdater(AbstractChannelHandlerContext.class, "handlerState");
  • 状态一共分为 4 种: INIT,ADD_PENDING,ADD_COMPLETEREMOVE_COMPLETE
  • 通过 handlerStateHANDLER_STATE_UPDATER, 采用 CAS 的方式原子化更新属性,这样就不用加锁处理并发问题。

1.3 不可变的属性

即被 final 修饰的属性,在创建 ChannelHandlerContext 对象时就需要赋值。

  1. DefaultChannelPipeline pipeline

    当前上下文所属的管道 pipeline, 而且它的类型就定死了是 DefaultChannelPipeline 类。

  2. name 表示上下文的名称
  3. ordered 表示上下文的执行器是不是有序的
  4. executor 上下文的执行器

    如果这个值是 null,那么上下文的执行器用的就是所属通道 Channel 的事件轮询器。

  5. executionMask 表示事件执行器 ChannelHandler 的执行标记

    用来判断是否跳过执行器 ChannelHandler 的某些事件处理方法。

二. 重要方法

2.1 构造方法

    AbstractChannelHandlerContext(DefaultChannelPipeline pipeline, EventExecutor executor,
                                  String name, Class<? extends ChannelHandler> handlerClass) {
        this.name = ObjectUtil.checkNotNull(name, "name");
        this.pipeline = pipeline;
        this.executor = executor;
        // 调用 ChannelHandlerMask.mask(handlerClass) 方法,获取执行标记
        this.executionMask = mask(handlerClass);
        // 表示上下文的事件执行器是不是有序的,即以有序/串行的方式处理所有提交的任务。
        // executor == null,说明当前上下文用的是通道Channel的 channel().eventLoop(),这个肯定是有序的
        ordered = executor == null || executor instanceof OrderedEventExecutor;
    }

这个是 AbstractChannelHandlerContext 唯一的构造方法,基本上赋值了它所有的 final 属性。

2.2 状态相关方法

  1. 等待添加

     final void setAddPending() {
         boolean updated = HANDLER_STATE_UPDATER.compareAndSet(this, INIT, ADD_PENDING);
         // 这应该总是为真,因为它必须在 setAddComplete()或 setRemoved()之前调用。
         assert updated;
     }
    

    将上下文的状态变成等待添加状态 ADD_PENDING

  2. 已添加

     final boolean setAddComplete() {
         for (;;) {
             int oldState = handlerState;
             if (oldState == REMOVE_COMPLETE) {
                 return false;
             }
             // 确保当 handlerState 已经是REMOVE_COMPLETE时,我们永远不会更新。
             // oldState 通常是 ADD_PENDING,但当使用不公开排序保证的 EventExecutor 时,也可能是 REMOVE_COMPLETE。
             if (HANDLER_STATE_UPDATER.compareAndSet(this, oldState, ADD_COMPLETE)) {
                 return true;
             }
         }
     }
    

    通过for (;;) 死循环,采用 CAS 的方法,将上下文的状态变成已添加ADD_COMPLETE
    只有已添加状态上下文的事件处理器 ChannelHandler 才能处理事件。

     final void callHandlerAdded() throws Exception {
    
         // 我们必须在调用 handlerAdded 之前调用 setAddComplete,将状态改成 REMOVE_COMPLETE,
         // 否则这个上下文对应的事件处理器将不会处理任何事件,因为状态不允许。
         if (setAddComplete()) {
             handler().handlerAdded(this);
         }
     }
    

    将状态变成已添加,如果设置成功就调用 handler().handlerAdded(this) 方法,通知事件处理器已经被添加到管道上了。

  3. 已删除

     final void setRemoved() {
         handlerState = REMOVE_COMPLETE;
     }
    
     final void callHandlerRemoved() throws Exception {
         try {
             // 只有 handlerState 状态变成 ADD_COMPLETE 时,才会调用 handler().handlerRemoved(this);
             // 也就是说 只有之前调用过 handlerAdded(…)方法,之后才会调用handlerRemoved(…) 方法。
             if (handlerState == ADD_COMPLETE) {
                 handler().handlerRemoved(this);
             }
         } finally {
             // 在任何情况下都要将该上下文标记为已删除
             setRemoved();
         }
     }
    
    • 将上下文状态变成已删除。
    • 如果上下文状态之前的状态是已添加,那么就会调用 handler().handlerRemoved(this) 方法。
    • 也就是说,只有之前调用过 handlerAdded(…)方法,之后才会调用handlerRemoved(…) 方法。

2.3 发送IO事件

2.3.1 发送入站 IO事件

    /**
     * 发送注册的IO事件
     */
    @Override
    public ChannelHandlerContext fireChannelRegistered() {
        // 通过 findContextInbound 方法找到下一个入站处理器上下文
        // 通过 invokeChannelRegistered 方法,调用下一个入站处理器的对应事件处理方法
        invokeChannelRegistered(findContextInbound(MASK_CHANNEL_REGISTERED));
        return this;
    }

    /**
     * 保证在上下文 next 的事件执行器线程中调用对应方法
     */
    static void invokeChannelRegistered(final AbstractChannelHandlerContext next) {
        EventExecutor executor = next.executor();
        if (executor.inEventLoop()) {
            // 如果当前线程是 next 事件执行器EventExecutor线程,直接调用
            next.invokeChannelRegistered();
        } else {
            // 如果当前线程不是 next 事件执行器线程,
            // 那么就通过事件执行器EventExecutor 的execute方法,
            // 保证在执行器线程调用
            executor.execute(new Runnable() {
                @Override
                public void run() {
                    next.invokeChannelRegistered();
                }
            });
        }
    }

    /**
     * 调用该上下文拥有的事件处理器 ChannelHandler 的对应方法
     */
    private void invokeChannelRegistered() {
        // 判断当前上下文有没有已经添加到管道上了
        if (invokeHandler()) {
            // 如果已经添加完成了,就调用对应事件处理器方法
            try {
                ((ChannelInboundHandler) handler()).channelRegistered(this);
            } catch (Throwable t) {
                invokeExceptionCaught(t);
            }
        } else {
            // 如果没有添加完成,即状态不是 ADD_COMPLETE,
            // 就继续调用 fire 的方法,让管道下一个处理器处理
            fireChannelRegistered();
        }
    }

我们以注册的 IO 事件为例,发现调用过程:

  1. 先通过 findContextInbound 方法,找到下一个入站处理器上下文。
  2. 再调用 invokeChannel... 系列静态方法,保证处理器方法的调用是在它的上下文事件执行器 EventExecutor 线程中。
  3. 最后通过 invokeChannel... 系列成员方法,调用该上下文拥有的事件处理器 ChannelHandler 的对应方法。

    要通过 invokeHandler() 方法判断该上下文是否已经添加到管道上,只有已经完全添加的上下文才能处理事件。

2.3.2 发送出站IO操作

    /**
     * 发送绑定 IO 操作
     */
    @Override
    public ChannelFuture bind(final SocketAddress localAddress, final ChannelPromise promise) {
        ObjectUtil.checkNotNull(localAddress, "localAddress");
        // 检查 promise 是否有效
        if (isNotValidPromise(promise, false)) {
            // 返回 true, 说明已取消,直接返回,不做下面的处理
            return promise;
        }

        // 通过 findContextOutbound  方法找到上一个出站处理器上下文
        final AbstractChannelHandlerContext next = findContextOutbound(MASK_BIND);
        // 保证在上下文 next 的事件执行器线程中调用对应方法
        EventExecutor executor = next.executor();
        if (executor.inEventLoop()) {
            next.invokeBind(localAddress, promise);
        } else {
            safeExecute(executor, new Runnable() {
                @Override
                public void run() {
                    next.invokeBind(localAddress, promise);
                }
            }, promise, null, false);
        }
        return promise;
    }

    private void invokeBind(SocketAddress localAddress, ChannelPromise promise) {
        // 判断当前上下文有没有已经添加到管道上了
        if (invokeHandler()) {
            // 如果已经添加完成了,就调用对应事件处理器方法
            try {
                ((ChannelOutboundHandler) handler()).bind(this, localAddress, promise);
            } catch (Throwable t) {
                notifyOutboundHandlerException(t, promise);
            }
        } else {
            // 如果没有添加完成,即状态不是 ADD_COMPLETE,
            // 就继续调用 fire 的方法,让管道下一个处理器处理
            bind(localAddress, promise);
        }
    }

我们以绑定 IO 事件为例,你会发现调用流程和入站事件差不多,只不过出站事件没有中间那个静态方法。

2.3.3 invokeTasks 作用

你会发现有的入站和出站事件的处理,与上面的流程不一样,有四个事件:

  • channelReadComplete 读完成的入站事件
  • channelWritabilityChanged 可读状态改变的入站事件
  • read 设置读的出站事件
  • flush 刷新数据的出站事件
    public ChannelHandlerContext fireChannelReadComplete() {
        invokeChannelReadComplete(findContextInbound(MASK_CHANNEL_READ_COMPLETE));
        return this;
    }

    static void invokeChannelReadComplete(final AbstractChannelHandlerContext next) {
        EventExecutor executor = next.executor();
        if (executor.inEventLoop()) {
            next.invokeChannelReadComplete();
        } else {
            Tasks tasks = next.invokeTasks;
            if (tasks == null) {
                next.invokeTasks = tasks = new Tasks(next);
            }
            executor.execute(tasks.invokeChannelReadCompleteTask);
        }
    }

    private void invokeChannelReadComplete() {
        if (invokeHandler()) {
            try {
                ((ChannelInboundHandler) handler()).channelReadComplete(this);
            } catch (Throwable t) {
                invokeExceptionCaught(t);
            }
        } else {
            fireChannelReadComplete();
        }
    }

你会发现发送读完成事件的处理过程和上面有区别,不同的是:

  • 上面是通过 executor.execute(new Runnable()),每次都创建新的 Runnable 对象。
  • 而这里是通过一个 invokeTasks 对象,不用每次都创建新的 Runnable 对象,减少对象创建的实例。
 private static final class Tasks {
        private final AbstractChannelHandlerContext next;
        // `channelReadComplete` 读完成的入站事件
        private final Runnable invokeChannelReadCompleteTask = new Runnable() {
            @Override
            public void run() {
                next.invokeChannelReadComplete();
            }
        };

        // `read` 设置读的出站事件
        private final Runnable invokeReadTask = new Runnable() {
            @Override
            public void run() {
                next.invokeRead();
            }
        };

        // `channelWritabilityChanged` 可读状态改变的入站事件
        private final Runnable invokeChannelWritableStateChangedTask = new Runnable() {
            @Override
            public void run() {
                next.invokeChannelWritabilityChanged();
            }
        };

        // `flush` 刷新数据的出站事件
        private final Runnable invokeFlushTask = new Runnable() {
            @Override
            public void run() {
                next.invokeFlush();
            }
        };

        Tasks(AbstractChannelHandlerContext next) {
            this.next = next;
        }
    }

为什么这四个事件可以呢?

  • 你仔细观察,这四个事件都没有参数,也就是说每次调用的时候,没有变化。
  • 也许你会说 channelRegistered,channelUnregistered,channelActivechannelInactive 这几个事件也没有参数啊,为什么它们不这么照着上面的处理呢?主要是因为这几个比较特殊,它们只会调用一次,所以没有必要那么处理。

2.3.4 写操作

写操作的处理过程也和上面的不是太一样,多了点特别处理。

我们知道写操作 write 和 刷新操作flush 是一对的,不调用刷新的话,写入的数据永远不会发送到远端。

2.3.4.1 刷新操作

  /**
     * 在管道中寻找下一个事件处理器 进行刷新的IO操作
     */
    @Override
    public ChannelHandlerContext flush() {
        final AbstractChannelHandlerContext next = findContextOutbound(MASK_FLUSH);
        EventExecutor executor = next.executor();
        if (executor.inEventLoop()) {
            next.invokeFlush();
        } else {
            // 因为刷新操作没有参数,不用每次都创建新的 Runnable 实例,
            // 直接复用
            Tasks tasks = next.invokeTasks;
            if (tasks == null) {
                next.invokeTasks = tasks = new Tasks(next);
            }
            safeExecute(executor, tasks.invokeFlushTask, channel().voidPromise(), null, false);
        }

        return this;
    }

    /**
     * 刷新
     */
    private void invokeFlush() {
        if (invokeHandler()) {
            // 调用当前上下文对应处理器的 flush 处理方法
            invokeFlush0();
        } else {
            // 当前上下文对应的处理器不处理,
            // 继续在管道中寻找下一个事件处理器处理
            flush();
        }
    }

    private void invokeFlush0() {
        try {
            ((ChannelOutboundHandler) handler()).flush(this);
        } catch (Throwable t) {
            invokeExceptionCaught(t);
        }
    }
  • 寻找下一个事件处理器
  • 保证 invokeFlush 方法调用在下一个事件处理器的执行器线程中;又因为刷新操作没有额外参数,不用每次都创建新的 Runnable 实例,直接复用 invokeTasks
  • 通过 invokeHandler() 判断是否跳过当前事件处理器的处理方法。

2.3.4.2 写入操作

    /**
     * 在管道中寻找下一个事件处理器 进行写入的IO操作
     */
    @Override
    public ChannelFuture write(final Object msg, final ChannelPromise promise) {
        // 写入消息,不刷新
        write(msg, false, promise);

        return promise;
    }

    /**
     * 在管道中寻找下一个事件处理器 进行写入并刷新的IO操作
     */
    @Override
    public ChannelFuture writeAndFlush(Object msg, ChannelPromise promise) {
        // 写入消息并且刷新
        write(msg, true, promise);
        return promise;
    }

提供写入和写入并刷新两个方法。它们都调用了 write(Object, boolean, ChannelPromise) 方法。

    private void write(Object msg, boolean flush, ChannelPromise promise) {
        ObjectUtil.checkNotNull(msg, "msg");
        try {
            // 检查 promise 是否有效
            if (isNotValidPromise(promise, true)) {
                // 回收引用
                ReferenceCountUtil.release(msg);
                // cancelled
                return;
            }
        } catch (RuntimeException e) {
            // 回收引用
            ReferenceCountUtil.release(msg);
            throw e;
        }

        // 通过 findContextOutbound  方法找到上一个出站处理器上下文
        final AbstractChannelHandlerContext next = findContextOutbound(flush ?
                (MASK_WRITE | MASK_FLUSH) : MASK_WRITE);
        // 只是添加附加信息,在内存泄露的时候,可以获取到这个附加信息
        final Object m = pipeline.touch(msg, next);
        EventExecutor executor = next.executor();
        if (executor.inEventLoop()) {
            // 在当前上下文线程中
            if (flush) {
                // 如果包括刷新,就调用 invokeWriteAndFlush 方法
                next.invokeWriteAndFlush(m, promise);
            } else {
                // 如果不包括刷新,就调用 invokeWrite 方法
                next.invokeWrite(m, promise);
            }
        } else {
            // 将写操作封装成一个 WriteTask,也是一个 Runnable 子类。
            final WriteTask task = WriteTask.newInstance(next, m, promise, flush);
            if (!safeExecute(executor, task, promise, m, !flush)) {
                // We failed to submit the WriteTask. We need to cancel it so we decrement the pending bytes
                // and put it back in the Recycler for re-use later.
                //
                // See https://github.com/netty/netty/issues/8343.
                task.cancel();
            }
        }
    }
  • 这个方法流程和之前的流程没有区别,唯一不同的就是它创建了一个 WriteTask 对象,而不是一个简单的 Runnable 实例。
  • 因为写操作比较特殊,我们需要控制等待写入数据的大小,当等待写入数据太多,那么发送 channelWritabilityChanged 入站IO事件,告诉用户当前通道不可写了,先将缓冲区数据发送到远端。
  • 如何知道等待写入数据的大小?就是通过这个 WriteTask 类事件,计算写入对象 m 的大小,累加加入到写入数据的大小中。

2.3.5 查找下一个处理器上下文

  1. findContextInbound(int mask)
     private AbstractChannelHandlerContext findContextInbound(int mask) {
         AbstractChannelHandlerContext ctx = this;
         EventExecutor currentExecutor = executor();
         do {
             ctx = ctx.next;
             // 通过 MASK_ONLY_INBOUND 表示查找的是入站事件
             // mask 代表处理的方法,是否需要被跳过
         } while (skipContext(ctx, currentExecutor, mask, MASK_ONLY_INBOUND));
         return ctx;
     }
    
  2. findContextOutbound
     private AbstractChannelHandlerContext findContextOutbound(int mask) {
         AbstractChannelHandlerContext ctx = this;
         EventExecutor currentExecutor = executor();
         do {
             ctx = ctx.prev;
             // 通过 MASK_ONLY_OUTBOUND 表示查找的是出站事件
             // mask 代表处理的方法,是否需要被跳过
         } while (skipContext(ctx, currentExecutor, mask, MASK_ONLY_OUTBOUND));
         return ctx;
     }
    
  3. skipContext
     private static boolean skipContext(
             AbstractChannelHandlerContext ctx, EventExecutor currentExecutor, int mask, int onlyMask) {
         // Ensure we correctly handle MASK_EXCEPTION_CAUGHT which is not included in the MASK_EXCEPTION_CAUGHT
         // 这个方法返回 true,表示跳过这个 ctx,继续从管道中查找下一个。
         // 因为使用的是 || 或逻辑符,两个条件只要有一个为 true,就返回 true。
         // (ctx.executionMask & (onlyMask | mask)) == 0 表示这个 ctx 属于入站事件还是出站事件
         // (ctx.executor() == currentExecutor && (ctx.executionMask & mask) == 0)
         // 只有当 EventExecutor 相同的时候,才会考虑是否跳过 ctx,因为我们要保证事件处理的顺序。
         return (ctx.executionMask & (onlyMask | mask)) == 0 ||
                 // We can only skip if the EventExecutor is the same as otherwise we need to ensure we offload
                 // everything to preserve ordering.
                 // See https://github.com/netty/netty/issues/10067
                 (ctx.executor() == currentExecutor && (ctx.executionMask & mask) == 0);
     }
    

    这个方法代码很简单,但是逻辑很有意思。

    • 方法的返回值表示是否需要跳过这个上下文 ctx,返回 true 则跳过。
    • (ctx.executionMask & (onlyMask | mask)) == 0 ,如果等于 true,那就表明当前这个上下文的执行标记 executionMask 就没有 (onlyMask | mask) 中的任何方法啊,很容易就判断它不属于入站事件或者出站事件。
    • 第二个条件是判断上下文的执行标记是否包含这个方法 ctx.executionMask & mask,如果包含结果就不是 1, 不包含就是 0 (表示跳过,返回 true),所以当 (ctx.executionMask & mask) == 0 的时候,返回 true, 跳过这个上下文ctx,寻找下一个。
    • 不过这里多了一个 ctx.executor() == currentExecutor 判断, 为了事件处理的顺序性,如果事件执行器线程不一样,那么不允许跳过处理器方法,即使这个方法被 @Skip 注解也没用。

2.3.6 invokeHandler

    /**
     * 尽最大努力检测 ChannelHandler.handlerAdded(ChannelHandlerContext) 是否被调用。
     * 如果没有被调用则返回false,如果调用或无法检测返回true。
     *
     * 如果这个方法返回false,我们将不调用ChannelHandler,而只是转发事件,调用管道中下一个 ChannelHandler 处理。
     *
     * 因为可能管道DefaultChannelPipeline已经将这个 ChannelHandler放在链接列表中,
     * 但没有调用 ChannelHandler.handlerAdded(ChannelHandlerContext) 方法,
     * 有可能用户在 ChannelHandler.handlerAdded 中做了一些初始化操作,当它没有被调用时,
     * 不能将 IO 事件交个这个 ChannelHandler 处理。
     */
    private boolean invokeHandler() {
        // Store in local variable to reduce volatile reads.
        int handlerState = this.handlerState;
        return handlerState == ADD_COMPLETE || (!ordered && handlerState == ADD_PENDING);
    }

一般情况下,必须当上下文状态是 ADD_COMPLETE 才返回 true
但是如果上下文的事件执行器是顺序的,那么当上下文状态是 ADD_PENDING 就可以返回 true 了。

三. WriteTask 类

    static final class WriteTask implements Runnable {
        // 使用一个对象池,复用 WriteTask 实例
        private static final ObjectPool<WriteTask> RECYCLER = ObjectPool.newPool(new ObjectCreator<WriteTask>() {
            @Override
            public WriteTask newObject(Handle<WriteTask> handle) {
                return new WriteTask(handle);
            }
        });

        // 通过静态方法得到 WriteTask 实例
        static WriteTask newInstance(AbstractChannelHandlerContext ctx,
                Object msg, ChannelPromise promise, boolean flush) {
            // 从对象池中获取 WriteTask 实例
            WriteTask task = RECYCLER.get();
            // 初始化
            init(task, ctx, msg, promise, flush);
            return task;
        }

        private static final boolean ESTIMATE_TASK_SIZE_ON_SUBMIT =
                SystemPropertyUtil.getBoolean("io.netty.transport.estimateSizeOnSubmit", true);

        // Assuming compressed oops, 12 bytes obj header, 4 ref fields and one int field
        private static final int WRITE_TASK_OVERHEAD =
                SystemPropertyUtil.getInt("io.netty.transport.writeTaskSizeOverhead", 32);

        private final Handle<WriteTask> handle;
        // 当前上下文对象
        private AbstractChannelHandlerContext ctx;
        // 写入的数据对象
        private Object msg;
        private ChannelPromise promise;
        // 写入数据的大小
        private int size; // sign bit controls flush

        @SuppressWarnings("unchecked")
        private WriteTask(Handle<? extends WriteTask> handle) {
            this.handle = (Handle<WriteTask>) handle;
        }

        protected static void init(WriteTask task, AbstractChannelHandlerContext ctx,
                                   Object msg, ChannelPromise promise, boolean flush) {
            task.ctx = ctx;
            task.msg = msg;
            task.promise = promise;

            // 是否需要估算写入数据大小
            if (ESTIMATE_TASK_SIZE_ON_SUBMIT) {
                // 估算数据大小
                task.size = ctx.pipeline.estimatorHandle().size(msg) + WRITE_TASK_OVERHEAD;
                // 增加等待写入数据的大小
                ctx.pipeline.incrementPendingOutboundBytes(task.size);
            } else {
                task.size = 0;
            }
            if (flush) {
                // 将size 大小变成负数,那么就会调用 写入并刷新的方法
                task.size |= Integer.MIN_VALUE;
            }
        }

        @Override
        public void run() {
            try {
                // 减小等待写入数据大小
                decrementPendingOutboundBytes();
                if (size >= 0) {
                    // 只调用写入操作
                    ctx.invokeWrite(msg, promise);
                } else {
                    // 当 size < 0 ,写入并刷新操作
                    ctx.invokeWriteAndFlush(msg, promise);
                }
            } finally {
                recycle();
            }
        }

        void cancel() {
            try {
                // 取消的话,也需要减小等待写入数据大小
                decrementPendingOutboundBytes();
            } finally {
                recycle();
            }
        }

        /**
         * 减小等待写入数据大小
         */
        private void decrementPendingOutboundBytes() {
            if (ESTIMATE_TASK_SIZE_ON_SUBMIT) {
                ctx.pipeline.decrementPendingOutboundBytes(size & Integer.MAX_VALUE);
            }
        }

        private void recycle() {
            // Set to null so the GC can collect them directly
            ctx = null;
            msg = null;
            promise = null;
            handle.recycle(this);
        }
    }

这个类的实现很简单

  • 使用静态方法从对象池中获取WriteTask 实例(这样会复用WriteTask)
  • 每次调用初始化 init 方法,如果配置项 ESTIMATE_TASK_SIZE_ON_SUBMITtrue,都会增加等待写入数据的大小。
  • 真正运行(run被调用),或者取消的时候,都会减少等待写入数据的大小。

DefaultChannelPipeline

    @UnstableApi
    protected void incrementPendingOutboundBytes(long size) {
        ChannelOutboundBuffer buffer = channel.unsafe().outboundBuffer();
        if (buffer != null) {
            buffer.incrementPendingOutboundBytes(size);
        }
    }

    @UnstableApi
    protected void decrementPendingOutboundBytes(long size) {
        ChannelOutboundBuffer buffer = channel.unsafe().outboundBuffer();
        if (buffer != null) {
            buffer.decrementPendingOutboundBytes(size);
        }
    }

都是调用 ChannelOutboundBuffer 类的对应方法。

ChannelOutboundBuffer

    void incrementPendingOutboundBytes(long size) {
        incrementPendingOutboundBytes(size, true);
    }

    private void incrementPendingOutboundBytes(long size, boolean invokeLater) {
        if (size == 0) {
            return;
        }

        long newWriteBufferSize = TOTAL_PENDING_SIZE_UPDATER.addAndGet(this, size);
        if (newWriteBufferSize > channel.config().getWriteBufferHighWaterMark()) {
            setUnwritable(invokeLater);
        }
    }

    private void setUnwritable(boolean invokeLater) {
        for (;;) {
            final int oldValue = unwritable;
            final int newValue = oldValue | 1;
            if (UNWRITABLE_UPDATER.compareAndSet(this, oldValue, newValue)) {
                if (oldValue == 0) {
                    fireChannelWritabilityChanged(invokeLater);
                }
                break;
            }
        }
    }

    void decrementPendingOutboundBytes(long size) {
        decrementPendingOutboundBytes(size, true, true);
    }

    private void decrementPendingOutboundBytes(long size, boolean invokeLater, boolean notifyWritability) {
        if (size == 0) {
            return;
        }

        long newWriteBufferSize = TOTAL_PENDING_SIZE_UPDATER.addAndGet(this, -size);
        if (notifyWritability && newWriteBufferSize < channel.config().getWriteBufferLowWaterMark()) {
            setWritable(invokeLater);
        }
    }

    private void setWritable(boolean invokeLater) {
        for (;;) {
            final int oldValue = unwritable;
            final int newValue = oldValue & ~1;
            if (UNWRITABLE_UPDATER.compareAndSet(this, oldValue, newValue)) {
                if (oldValue != 0 && newValue == 0) {
                    fireChannelWritabilityChanged(invokeLater);
                }
                break;
            }
        }
    }

ChannelOutboundBuffer 中有一个 totalPendingSize 变量表示写缓冲区等待数据大小。

  • 当它的值大于 channel.config().getWriteBufferHighWaterMark() 时,表示不能写了,通过 setUnwritable(invokeLater) 发送当前通道可写状态改变的 入站IO事件。
  • 当它的值小于 channel.config().getWriteBufferLowWaterMark() 时,表示又可以写了,通过 setWritable(invokeLater) 发送当前通道可写状态改变的 入站IO事件。

四. 总结

ChannelHandlerContext 的主要实现原理已经介绍完毕了,你也明白了,它是如何向上游或下游传递事件了。

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 204,530评论 6 478
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 86,403评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 151,120评论 0 337
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,770评论 1 277
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,758评论 5 367
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,649评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,021评论 3 398
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,675评论 0 258
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,931评论 1 299
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,659评论 2 321
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,751评论 1 330
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,410评论 4 321
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,004评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,969评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,203评论 1 260
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 45,042评论 2 350
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,493评论 2 343

推荐阅读更多精彩内容