netty源碼分析(22)- outBound事件的傳播

上一節學習了inbound事件的傳播,充分理解了在pipeline中是如何向一個個handler傳播事件的,以channelRead事件也就是讀事件爲例,研究了其處理邏輯。

本節學習outbound事件的傳播,和inbound事件有相似之處。以write事件爲例,進行學習研究。

  • ChannelHandler家族
    如下圖,ChannelHandler家族分爲ChannelInboundHandler以及ChannelOutboundHandler,分別定義了入站處理器以及出站處理器,同時也提供了對應的實現。上一節inbound事件傳播也發現確定是inbound事件還是outbound事件是由instanceof關鍵子判斷是否實現了對應的接口。

對比一下ChannelInboundHandlerChannelOutboundHandler的方法,可以發現,ChannelInboundHandler的方法以被動觸發爲主,而ChannelOutboundHandler的方法則是主動行爲。

ChannelInboundHandler ChannelInboundHandler
channelRegistered(事件觸發:註冊) deregister(註銷)
channelUnregistered(事件觸發:註銷) disconnect(取消連接)
channelActive(事件觸發:激活連接) connect(連接)
channelRead(事件觸發:讀) read(讀)
channelReadComplete(事件觸發:讀完成) write(寫)
userEventTriggered(事件觸發:用戶事件) flush(刷緩存)
channelWritabilityChanged(事件觸發:通道可讀狀態被修改) close(關閉連接)
exceptionCaught(事件觸發:異常) bind(綁定端口)
  • ChannelOutboundHandler的執行順序正好和ChannelInboundHandler相反,是倒序的。
class DataServerInitializer extends ChannelInitializer<SocketChannel> {

    @Override
    protected void initChannel(SocketChannel ch) throws Exception {

        ch.pipeline().addLast(
                new OutboundHandlerA(),
                new OutboundHandlerB(),
                new OutboundHandlerC()
        );
    }
}


class OutboundHandlerA extends ChannelOutboundHandlerAdapter {

    @Override
    public void write(ChannelHandlerContext ctx, Object msg, ChannelPromise promise) throws Exception {
        System.out.println("OutboundHandlerA : " + msg);
        ctx.write(msg, promise);
    }
}

class OutboundHandlerB extends ChannelOutboundHandlerAdapter {

    @Override
    public void write(ChannelHandlerContext ctx, Object msg, ChannelPromise promise) throws Exception {
        System.out.println("OutboundHandlerB : " + msg);
        ctx.write(msg, promise);
    }

    @Override
    public void handlerAdded(ChannelHandlerContext ctx) throws Exception {
        ctx.executor().schedule(() -> {
            ctx.channel().write("hello world");
        }, 3, TimeUnit.SECONDS);
    }
}

class OutboundHandlerC extends ChannelOutboundHandlerAdapter {

    @Override
    public void write(ChannelHandlerContext ctx, Object msg, ChannelPromise promise) throws Exception {
        System.out.println("OutboundHandlerC : " + msg);
        ctx.write(msg, promise);
    }
}

改變添加handler的順序

class DataServerInitializer extends ChannelInitializer<SocketChannel> {

    @Override
    protected void initChannel(SocketChannel ch) throws Exception {

        ch.pipeline().addLast(
                new OutboundHandlerB(),
                new OutboundHandlerA(),
                new OutboundHandlerC()
        );
    }
}

  • 跟蹤ctx.channel().write("hello world");
    @Override
    public ChannelFuture write(Object msg) {
        //從pipeline開始調用
        return pipeline.write(msg);
    }

    @Override
    public final ChannelFuture write(Object msg) {
        //從尾節點開始傳播
        return tail.write(msg);
    }
    @Override
    public ChannelFuture write(Object msg) {
        //添加一個回調Promise,包裝channel和executor
        return write(msg, newPromise());
    }

最終調用到AbstractChannelHandlerContext#write()方法,主要是做了兩件事

  1. findContextOutbound方法找到下一個ChannelOutboundHandlerContext
  2. 判斷是否需要flush,選擇執行write回調方法之後是否執行flush回調方法
    private void write(Object msg, boolean flush, ChannelPromise promise) {
        ObjectUtil.checkNotNull(msg, "msg");
        try {
            if (isNotValidPromise(promise, true)) {
                ReferenceCountUtil.release(msg);
                // cancelled
                return;
            }
        } catch (RuntimeException e) {
            ReferenceCountUtil.release(msg);
            throw e;
        }
        //查找下一個ChannelOutboundHandlerContext
        AbstractChannelHandlerContext next = findContextOutbound();
        final Object m = pipeline.touch(msg, next);
        EventExecutor executor = next.executor();
        if (executor.inEventLoop()) {
            //判斷是否刷新
            if (flush) {
                //執行寫並刷新方法
                next.invokeWriteAndFlush(m, promise);
            } else {
                //執行寫方法
                next.invokeWrite(m, promise);
            }
        } else {
            final AbstractWriteTask task;
            if (flush) {
                task = WriteAndFlushTask.newInstance(next, m, promise);
            }  else {
                task = WriteTask.newInstance(next, m, promise);
            }
            if (!safeExecute(executor, task, promise, m)) {
                // We failed to submit the AbstractWriteTask. 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();
            }
        }
    }

  • findContextOutbound方法找到下一個ChannelOutboundHandlerContext
    private AbstractChannelHandlerContext findContextOutbound() {
        //循環往前查找,通過outbound屬性判斷
        AbstractChannelHandlerContext ctx = this;
        do {
            ctx = ctx.prev;
        } while (!ctx.outbound);
        return ctx;
    }
  • 執行write回調方法
    private void invokeWrite(Object msg, ChannelPromise promise) {
        //判斷handler的狀態是可以執行回調函數的
        if (invokeHandler()) {
            invokeWrite0(msg, promise);
        } else {
            write(msg, promise);
        }
    }

    private void invokeWrite0(Object msg, ChannelPromise promise) {
        try {
            //執行回調函數write
            ((ChannelOutboundHandler) handler()).write(this, msg, promise);
        } catch (Throwable t) {
            notifyOutboundHandlerException(t, promise);
        }
    }

  • invokeWriteAndFlush執行完write回調方法之後執行flush回調方法
    private void invokeWriteAndFlush(Object msg, ChannelPromise promise) {
        if (invokeHandler()) {
            //執行write
            invokeWrite0(msg, promise);
            //執行flush
            invokeFlush0();
        } else {
            writeAndFlush(msg, promise);
        }
    }

    private void invokeFlush0() {
        try {
            //回調flush方法
            ((ChannelOutboundHandler) handler()).flush(this);
        } catch (Throwable t) {
            notifyHandlerException(t);
        }
    }
  • 通過跟蹤源碼,也不難發現無論是從tail節點開始還是從當前節點開始調用write方法,最終都會到head節點。而頭節點正是使用unsafe來具體完成這些操作的。
        @Override
        public void write(ChannelHandlerContext ctx, Object msg, ChannelPromise promise) {
            unsafe.write(msg, promise);
        }

        @Override
        public void flush(ChannelHandlerContext ctx) {
            unsafe.flush();
        }
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章