netty EventLoop write() 源碼分析(二)

netty 高併發物聯網交流羣 651219170

Netty 的write() 和 flush()

下面我們就跟蹤下 write() 和 flush() .驗證兩個事情。
1.在非 EventLoop 線程 write() 是線程安全的。
2.write() 只是把數據放到了 ChannelOutboundBuffer 中。flush() 纔是把數據發送出去(在 EventLoop 線程調用的話,可以直接發送出去。否則把 flushedEntry 指向爲非 null,並註冊可寫事件,等待 EventLoop 線程發送出去)。

ctx.write(“recvied message”) 開始向下跟蹤最終發現不管是 write() 還是 writeAndFlush() 都是調用了AbstractChannelHandlerContext 的 write() 方法。
write() 就是獲取下一個 Outbuondhandler 然後調用 write()

 private void write(Object msg, boolean flush, ChannelPromise promise) {
        //找到註冊的(addFirst)下一個 ChannelOutBound 的 context
        AbstractChannelHandlerContext next = findContextOutbound();
        final Object m = pipeline.touch(msg, next);
        EventExecutor executor = next.executor();

        //如果當前 write() 是 eventLoop 線程則執行 write() 操作,否則添加一個寫任務。
        if (executor.inEventLoop()) {
            if (flush) {
                next.invokeWriteAndFlush(m, promise);
            } else {
                next.invokeWrite(m, promise);
            }
        } else {
            AbstractWriteTask task;
            if (flush) {
                task = WriteAndFlushTask.newInstance(next, m, promise);
            }  else {
                task = WriteTask.newInstance(next, m, promise);
            }
            safeExecute(executor, task, promise, m);
        }
    }

在 EventLoop 線程調用 write() 和 flush()

調用的 handler 的 write()

在上節的代碼可以看出來我們在 EventLoop 線程調用 write() 和非 EventLoop 調用 write() 是有一個分支的(和明顯,在 EventLoop 線程的話不需要考慮競爭問題)。

    private void invokeWrite(Object msg, ChannelPromise promise) {
        //檢查下 handlerAdded 是否調用完畢(包括其他的一些狀態)
        if (invokeHandler()) {
            //調用這個 handler 的 wirte() 這個 handler 就是 ChannelOutboundHandler
            invokeWrite0(msg, promise);
        } else {
            //否則就直接調用下個 outboundhandler 的 write()
            write(msg, promise);
        }
    }

    private void invokeWrite0(Object msg, ChannelPromise promise) {
        try {
            //最後調用的是 HeadContext 的 wrire()
            ((ChannelOutboundHandler) handler()).write(this, msg, promise);
        } catch (Throwable t) {
            notifyOutboundHandlerException(t, promise);
        }
    }

HeadContext 的 write()
調用當前這個 handdler 的 write() 。我們自定義的 handdler 一般都會調用 supper.write() 來調用下個 handdler 的 write()。當鏈表到達頭部的時候就會調用 netty 定義的 HeadContext 的 write()(因爲是 write() 是出站,所以最後調用的是 HeadContext)
出站流程
此圖借用 netty in action
所以查看這HeadContext.write()->AbstractUnsafe.write()
在這個方法裏可以看到。msg 被加到了 ChannelOutboundBuffer 裏面。

Netty 線程調用 flush()

下面我們來看下。flush() 做了什麼。
和 write() 一樣的出站。會先調用我們的 write() 最後調用 HeadContext 的 flush().
HeadContext.flush()->AbstractUnsafe.flush()

  public final void flush() {
            assertEventLoop();

            ChannelOutboundBuffer outboundBuffer = this.outboundBuffer;
            if (outboundBuffer == null) {
                return;
            }
            //把 flushedEntry 只向了第一個未發送的 Entry
            outboundBuffer.addFlush();
            //在這裏調用了 doWrite()
            flush0();
        }

addFlush() 之前我們看過,讓 flushedEntry 指向了第一個爲發送的數據。做好發送準備。
flush0() 裏面調用了 doWrite() 這個方法在前面有詳細的介紹了。就是這個方法通過 channle 發送了數據。
我們這裏講的都是調用write() 和 flush() 在 EventLoop 線程所以直接發送是沒有線程競爭的(也就是說不需要先在 Selector 註冊個事件然後在發通過 EventLoop 線程發送出去)。

不在 EventLoop 線程調用 write()

不在 EventLoop 線程的封裝成兩個 task 然後調用 safeExecute() 執行就好了。safeExecute()的第一個參數就是 EventLoop 線程。所以說不在 EventLoop 線程調用 write() 或者 flush() 其實都是封裝成了一個任務然後調用了等待 EventLoop 線程調用。還記得之前我們在看 EventLoop 代碼的時候有個 ioRate 覺定了除了執行 IO 事件,還要執行 runAllTask() 。

          //獲取 EventLoop 線程
          EventExecutor executor = next.executor();
          AbstractWriteTask task;
            if (flush) {
                task = WriteAndFlushTask.newInstance(next, m, promise);
            }  else {
                task = WriteTask.newInstance(next, m, promise);
            }
            safeExecute(executor, task, promise, m);

WriteTask

先看看類圖。
WriteTask的類圖
既然是在線程裏執行那肯定得實現run() 方法。
這個 run 就在 AbstractWriteTask 裏。
run()->write()
wrire() 被子類 WriteTask 重新了。

protected void write(AbstractChannelHandlerContext ctx, Object msg, ChannelPromise promise) {
    ctx.invokeWrite(msg, promise);
}

其中 ctx.write()->調用了 AbstractChannelContext.write()之前已經加過了。最後就是把任務加到了 outboundBuffer 這個鏈表。

 private void invokeWrite(Object msg, ChannelPromise promise) {
        //檢查下 handlerAdded 是否調用完畢(包括其他的一些狀態)
        if (invokeHandler()) {
            //調用這個 handler 的 wirte()
            invokeWrite0(msg, promise);
        } else {
            //否則就遞歸調用處理下個 handler 的 write()
            write(msg, promise);
        }
    }

WriteAndFlushTask

WriteAndFlushTask 和 WriteTask 原理一樣。只是重寫了 write()

  public void write(AbstractChannelHandlerContext ctx, Object msg, ChannelPromise promise) {
            super.write(ctx, msg, promise);
            ctx.invokeFlush();
        }

在調用 write() 的基礎上多了一步調用 ctx.flush() 的操作。
ctx.flush() 爲什麼能夠真正把數據發送出去在上面說過了。這裏不再重複。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章