Netty 4.x – ByteBuf

一、ByteBuf與Java NIO Bufferhtml

ByteBuf則是Java NIO Buffer的新輪子,官方列出了一些ByteBuf的特性:java

  • 須要的話,能夠自定義buffer類型;
  • 經過組合buffer類型,可實現透明的zero-copy;
  • 提供動態的buffer類型,如StringBuffer同樣,容量是按需擴展;
  • 無需調用flip()方法;
  • 經常「often」比ByteBuffer快。

 

二、ByteBuf實現類數組

ByteBuf提供了一些較爲豐富的實現類,邏輯上主要分爲兩種:HeapByteBuf和DirectByteBuf,實現機制則分爲兩種:PooledByteBuf和UnpooledByteBuf,除了這些以外,Netty還實現了一些衍生ByteBuf(DerivedByteBuf),如:ReadOnlyByteBuf、DuplicatedByteBuf以及SlicedByteBuf。app

ByteBuf實現類的類圖以下:dom

HeapByteBuf和DirectByteBuf區別在於Buffer的管理方式:HeapByteBuf由Heap管理,Heap是Java堆的意思,內部實現直接採用byte[] array;DirectByteBuf使用是堆外內存,Direct應是採用Direct I/O之意,內部實現使用java.nio.DirectByteBuffoer。性能

PooledByteBuf和UnpooledByteBuf,UnpooledByteBuf實現就是普通的ByteBuf了,PooledByteBuf是4.x以後的新特性,稍後再說。this

DerivedByteBuf是ByteBuf衍生類,實現採用裝飾器模式對原有的ByteBuf進行了一些封裝。ReadOnlyByteBuf是某個ByteBuf的只讀引用;DuplicatedByteBuf是某個ByteBuf對象的引用;SlicedByteBuf是某個ByteBuf的部份內容。.net

SwappedByteBuf和CompositedByteBuf我以爲也算某種程度的衍生類吧,SwappedByteBuf封裝了一個ByteBuf對象和ByteOrder對象,實現某個ByteBuf對象序列的逆轉;CompositedByteBuf內部實現了一個ByteBuf列表,稱之爲組合ByteBuf,因爲不懂相關的技術業務,沒法理解該類的存在乎義(官方解釋:A user can save bulk memory copy operations using a composite buffer at the cost of relatively expensive random access.)。這兩個類從邏輯上彷佛徹底能夠繼承於DerivedByteBuf,Trustin大神爲啥如此設計呢?設計

三、簡要的ByteBuf的實現機制指針

ByteBuf有兩個指針,readerIndex和writerIndex,用以控制buffer數組的讀寫。讀邏輯較爲簡單,不考慮邊界的狀況下,就是`return array[readerIndex++];`。這裏簡要分析一下HeapByteBuf的讀邏輯。

1. AbstractByteBuf.ensureWritable(minWritableBytes);

2. calculateNewCapacity(writerIndex + minWritableBytes)

> 2.1 判斷是否超過可寫入容量 maxCapacity – writerIndex

> 2.2 超過則拋異常,不然計算新容量 writerIndex + minWritableBytes

> 2.3 判斷是否超過設定閾值(4MB),超過每次增長按閾值(4MB)遞增,不然

> 2.4 初始大小爲64字節(newCapacity),新容量超過newCapacity則翻倍,直到newCapacity大於新容量爲止

> 2.5 返回Min(newCapacity, maxCapacity);

3. UnpooledHeapByteBuf.capacity(newCapacity);

> 3.1 確保可訪問,有一個`引用計數`的機制,引用計數爲0,則拋異常(ensureAccessible)

> 3.2 常規操做:判斷是否越界

> 3.3 若是newCapacity比原容量大,則直接建立新數組,並設置。不然

> 3.4 若是readerIndex小於新容量,將readable bytes拷貝至新的數組,反之將readerIndex和writerIndex均設置爲newCapacity。

4. setByte(writerIndex++, value)

> 4.1 確保可訪問

> 4.2 設置

五、ByteBuf特殊機制

5.1 Pooled

4.x開發了Pooled Buffer,實現了一個高性能的buffer池,分配策略則是結合了buddy allocation和slab allocation的jemalloc變種,代碼在io.netty.buffer.PoolArena。暫未深刻研讀。

官方說提供瞭如下優點:

  • 頻繁分配、釋放buffer時減小了GC壓力;
  • 在初始化新buffer時減小內存帶寬消耗(初始化時不可避免的要給buffer數組賦初始值);
  • 及時的釋放direct buffer。

固然,官方也說了不保證沒有內存泄露,因此默認狀況下仍是採用的UnpooledByteBufAllocator。5.x還處於beta版,看它的「 new and noteworthy 」文檔也沒說有啥變化,哈哈哈哈,查看最新的「 new and noteworthy 」文檔,PooledByteBufAllocator已經設置爲默認的Allocator (revised in 2014-01-16)。

5.2 Reference Count

ByteBuf的生命週期管理引入了Reference Count的機制,感受讓我回到了CPP時代。能夠經過簡單的繼承SimpleChannelInboundHandler實現自動釋放reference count。SimpleChannelInboundHandler的事件方法以下,在消費完畢msg後,能夠AutoRelease之:

public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {

        boolean release = true;

        try {

            if (acceptInboundMessage(msg)) {

                @SuppressWarnings("unchecked")

                I imsg = (I) msg;

                messageReceived(ctx, imsg);

            } else {

                release = false;

                ctx.fireChannelRead(msg);

            }

        } finally {

            if (autoRelease && release) {

                ReferenceCountUtil.release(msg);

            }

        }

    }

這一小節能夠單獨拎出來和Pooled放在一塊兒深刻研讀研讀,有興趣的能夠先看看官方文檔: Reference counted objects

5.3 Zero Copy

Zero-copy與傳統意義的 zero-copy 不太同樣。傳統的zero-copy是IO傳輸過程當中,數據無需中內核態到用戶態、用戶態到內核態的數據拷貝,減小拷貝次數。而Netty的zero-copy則是徹底在用戶態,或者說傳輸層的zero-copy機制,能夠參考下圖。因爲協議傳輸過程當中,一般會有拆包、合併包的過程,通常的作法就是System.arrayCopy了,可是Netty經過ByteBuf.slice以及Unpooled.wrappedBuffer等方法拆分、合併Buffer無需拷貝數據。

如何實現zero-copy的呢。slice實現就是建立一個SlicedByteBuf對象,將this對象,以及相應的數據指針傳入便可,wrappedBuffer實現機制相似。

相關文章
相關標籤/搜索