Netty入門(一):ByteBuf


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

Netty系列索引:

1.Netty入門(一):ByteBuf

2.Netty入門(二):Channel

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,具有平台相關性。

ByteBufHolder

利用組合的方式對ByteBuf進行擴展。實際應用中我們經常發現,除了實際的數據負載之外,我們還需要存儲各種屬性值。HTTP 響應便是一個很好的例子,除了表示為字節的內容,還包括狀態碼、cookie 等。為了處理這種常見的用例,Netty 提供了 ByteBufHolder。ByteBufHolder 也為 Netty 的高級特性提供了支持,如緩沖區池化,其中可以從池中借用 ByteBuf,並且在需要時自動釋放。

ByteBufAllocator

為了降低分配和釋放內存的開銷,Netty 通過 ByteBufAllocator 實現了(ByteBuf 的)池化,它可以用來分配我們所描述過的任意類型的 ByteBuf 實例。Netty提供了兩種ByteBufAllocator的實現:PooledByteBufAllocator和UnpooledByteBufAllocator。前者池化了ByteBuf的實例以提高性能並最大限度地減少內存碎片。

ReferenceCounted

類似GC引用計數法,Netty中ByteBuf和ByteBufHolder都實現了該接口,其主要通過方法retain(release)來增加(減少)資源被引用的次數,當引用為0時,代表該資源可以被釋放。


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM