Netty之WaterMark水位線

    Netty裏write數據的時候,是有水位線處理的,只要我們設置了。

    Netty中write的時候,其實並沒有真正將數據寫到網絡上,而是寫到ChannelOutboundBuffer裏面緩存起來的,writeAndFlush的時候則會寫入網絡。如果我們設置了高低水位線,比如低水位線L、高水位線H,如果ChannelOutboundBuffer裏面的緩存size大小超過了高水位線H,那麼channel.isWritable()是false,即邏輯上的不可寫入;而ChannelOutboundBuffer裏面緩存的size低於L則channel.isWritable()變爲true,即邏輯上可寫入。爲什麼說邏輯上,因爲如果我們在寫入之前不判斷channel.isWritable,其實是可以寫入的。

    如果不設置高低水位線限制,可能會有問題,比如網絡慢,但是程序往寫入數據快,導致很多數據堆積在ChannelOutboundBuffer中,使得內存爆炸。

    Abstract.AbstractUnsafe中,如下:

  1.     AbstraceUnsafe中有個ChannelOutboundBuffer,write()時就是存在這個buffer裏面,數據結構是一維數組
  2.     filterOutboundMessage裏面,AbstractNioByteChannel裏面,會把ByteBuffer轉換爲DirectByteBuffer或者FileRegion,因爲ByteBuffer是JVM內存,轉換爲DirectByteBuffer或者FileRegion後,減輕JVM GC的壓力,同時可以使用zero copy的優點
  3.     estimatorHandle.size()裏面返還ByteBuffer中的byte個數,即發送的數據是多少byte
  4.     addMessage()裏面,如圖1所示,在一維鏈表的最後添加Entry,這個新的Entry就是我們新write的數據
  5.     addMessage()裏面,如List-2所示,TOTAL_PENDING_SIZE_UPDATER是一個AtomicLongFieldUpdater,把ChannelOutboundBuffer裏面的totalPendingSize加上新write的數據大小,如果大小highWaterMark值,則設置unwritabble

    List-1

public final void write(Object msg, ChannelPromise promise) {
    assertEventLoop();

    ChannelOutboundBuffer outboundBuffer = this.outboundBuffer;
    if (outboundBuffer == null) {
        // If the outboundBuffer is null we know the channel was closed and so
        // need to fail the future right away. If it is not null the handling of the rest
        // will be done in flush0()
        // See https://github.com/netty/netty/issues/2362
        safeSetFailure(promise, WRITE_CLOSED_CHANNEL_EXCEPTION);
        // release message now to prevent resource-leak
        ReferenceCountUtil.release(msg);
        return;
    }

    int size;
    try {
        msg = filterOutboundMessage(msg);
        size = pipeline.estimatorHandle().size(msg);
        if (size < 0) {
            size = 0;
        }
    } catch (Throwable t) {
        safeSetFailure(promise, t);
        ReferenceCountUtil.release(msg);
        return;
    }

    outboundBuffer.addMessage(msg, size, promise);
}

    

圖1

    List-2 

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

    AnstractUnsafe裏面flush時就是把ChannelOutboundBuffer裏面的Entry鏈表中未flush的Entry寫入到網絡中,即返回給調用方,所以writeAndFlush是寫入得到Entry鏈表中後,又觸發了flush操作,flush時寫入網絡後,會把totalPendingSize減去相應的大小,如果totalPengingSize的值小於lowWaterMark則將unwritable設置爲false,適合及時響應的場景。

 

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