Netty入門(一):ByteBuf

       網路數據的基本單位總是位元組。Java NIO 提供了 ByteBuffer 作為它的位元組容器,但是這個類使用起來過於複雜,而且也有些繁瑣。Netty 的 ByteBuffer 替代品是 ByteBuf,一個強大的實現,既解決了 JDK API 的局限性,又為網路應用程式的開發者提供了更好的 API

ByteBuf優勢

  • 它可以被用戶自定義的緩衝區類型擴展
  • 通過內置的複合緩衝區類型實現了透明的零拷貝
  • 容量可以按需增長
  • 在讀和寫這兩種模式之間切換不需要調用 ByteBuffer 的 flip()方法
  • 讀和寫使用了不同的索引
  • 支援方法的鏈式調用
  • 支援引用計數
  • 支援池化

ByteBuf實現原理

如圖ByteBuf通維護了兩個不同的索引:一個用於讀取,一個用於寫入。

當你從 ByteBuf 讀取時,它的 readerIndex 將會被遞增已經被讀取的位元組數。同樣地,當你寫入 ByteBuf 時,它的writerIndex 也會被遞增

當調用readBytes時,readIndex會相應移動length位,如果readIndex移動後大於writeIndex則會拋異常。

當調用writeBytes時,writeIndex會相應移動length位,且通過ensureWritable方法實現自動擴容

其他常用API

getBytes 獲取可讀位元組數組
setBytes 寫入位元組
discardReadBytes 廢棄已讀位元組
mark 標記index
reset 將index重置到之前標記的位置(配合mark使用)
isReadable 如果至少有一個位元組可供讀取,則返回 true
isWritable 如果至少有一個位元組可被寫入,則返回 true
readableBytes 返回可被讀取的位元組數
writableBytes 返回可被寫入的位元組數
capacity 返回 ByteBuf 可容納的位元組數。在此之後,它會嘗試再次擴展直到達到 maxCapacity()
maxCapacity 返回 ByteBuf 可以容納的最大位元組數
hasArray 如果 ByteBuf 由一個位元組數組支撐,則返回 true
array 如果 ByteBuf 由一個位元組數組支撐則返回該數組;否則,它將拋出一個UnsupportedOperationException 異常

ByteBuf緩衝分類

1、Heap buffer(堆緩衝區):

就是將數據存在JVM堆空間中,在沒有被池化的情況可以快速分配和釋放。

優點:由於數據是存儲在JVM堆中,因此可以快速的創建與快速的釋放,並且它提供了直接訪問內部位元組數組的方法。

缺點:每次讀寫數據時,都需要先將數據複製到直接緩衝區中再進行網路傳輸。

2、Direct buffer(直接緩衝區):

直接緩衝區,在堆外直接分配記憶體空間,直接緩衝區並不會佔用堆的容量空間,因為它是由作業系統在本地記憶體進行的數據分配。

優點:在使用Socket進行數據傳遞時,性能非常好,因為數據直接位於作業系統的本地記憶體中,所以不需要從JVM將數據複製到直接緩衝區中 。

缺點:因為Direct Buffer是直接在作業系統記憶體中的,所以記憶體空間的分配與釋放要比堆空間更加複雜,而且速度要慢一些。

注意:

如果你的數據包含在一個在堆上的分配的緩衝區中,那麼事實上,在通過套接字發送他之前,jvm將會在內部把你的緩衝區複製到一個直接緩衝區中;這樣分配釋放就比較浪費資源;

建議:

直接緩衝區並不支援通過位元組數組的方式來訪問數據。對於後端業務的消息編解碼來說,推薦使用HeapByteBuf;對於I/O通訊執行緒在讀寫緩衝區時,推薦使用DirectByteBuf;

3、Composite Buffer 複合緩衝區:

可以擁有以上兩種的緩衝區,通過一種聚合視圖來操作底層持有的多種類型Buffer。這種緩衝,jdk nio是沒有這種特性的。

ByteBuf主要實現類

pooled:池化,重用ByteBuf對象

Direct:直接記憶體,內部通過ByteBuffer實現,典型裝飾模式

Heap:堆記憶體,內部持有byte數組

(1)UnpooledDirectByteBuf:

在堆外進行記憶體分配的非記憶體池ByteBuf,內部持有ByteBuffer對象,相關操作委託給ByteBuffer實現。

(2)UnpooledHeapByteBuf:

基於堆記憶體分配非記憶體池ByteBuf,即內部持有byte數組。

(3)UnpooledUnsafeDirectByteBuf:

和另外一個類UnpooledDirectByteBuf差不多相同,區別在於UnpooledUnsafeDirectByteBuf內部使用基於PlatformDependent相關操作實現ByteBuf,依賴平台。

(4)ReadOnlyByteBufferBuf:

只讀ByteBuf,內部持有ByteBuffer對象,相關操作委託給ByteBuffer實現,該ByteBuf限內部使用;

(5)FixedCompositeByteBuf:

用於將多個ByteBuf組合在一起,形成一個虛擬的只讀ByteBuf對象,不允許寫入和動態擴展。內部使用Object[]將多個ByteBuf組合在一起,一旦FixedCompositeByteBuf對象構建完成,則不會被更改。

(6)CompositeByteBuf:

用於將多個ByteBuf組合在一起,形成一個虛擬的ByteBuf對象,支援讀寫和動態擴展。內部使用List組合多個ByteBuf。一般使用使用ByteBufAllocator的compositeBuffer()方法,Unpooled的工廠方法compositeBuffer()或wrappedBuffer(ByteBuf… buffers)創建CompositeByteBuf對象。

(7)PooledByteBuf:

基於記憶體池的ByteBuf,主要為了重用ByteBuf對象,提升記憶體的使用效率;適用於高負載,高並發的應用中。主要有PooledDirectByteBuf,PooledHeapByteBuf,PooledUnsafeDirectByteBuf三個子類,PooledDirectByteBuf是在堆外進行記憶體分配的記憶體池ByteBuf,PooledHeapByteBuf是基於堆記憶體分配記憶體池ByteBuf,PooledUnsafeDirectByteBuf也是在堆外進行記憶體分配的記憶體池ByteBuf,區別在於PooledUnsafeDirectByteBuf內部使用基於PlatformDependent相關操作實現ByteBuf,具有平台相關性。