網路資料的基本單位總是位元組,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,具有平臺相關性,
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時,代表該資源可以被釋放,
轉載請註明出處,本文鏈接:https://www.uj5u.com/houduan/291980.html
標籤:Java
