我們知道,網絡數據的基本單位總是字節。Java NIO提供了ByteBuffer作為它的字節容器,但是這個類使用起來過於復雜,而且也有些繁瑣。
Netty的ByteBuffer替代品是ByteBuf,一個強大的實現,既解決了JDK API的局限性,又為網絡應用程序的開發者提供了更好的API。
ByteBuf的API
Netty的數據處理API通過兩個組件暴露——abstract class ByteBuf和interface ByteBufHolder。
下面是一些ByteBuf API的優點:
- 它可以被用戶自定義的緩沖區類型擴展;
- 通過內置的復合緩沖區類型實現了透明的零拷貝;
- 容量可以按需增長(類似於JDK的StringBuilder);
- 在讀和寫這兩種模式之間切換不需要調用ByteBuffer的flip()方法;
- 讀和寫使用了不同的索引;
- 支持方法的鏈式調用;
- 支持引用計數;
- 支持池化。
其他可以用於管理ByteBuffer實例的分配,以及執行各種針對於數據容器本身和它所持有的數據的操作。
因為所有的網絡通信都涉及字節序列的移動,所以高效易用的數據結構明顯是必不可少的。Nety的ByteBuf實現滿足並超越了這些需求。
ByteBuf維護了兩個不同的索引:一個用於讀取,一個用於寫入。當你從ByteBuf讀取時,它的readIndex將會被遞增已經被讀取的字節數。同樣的,當你寫入ByteBuf時,它的writeIndex也會被遞增。
要了解這些索引兩兩之間的關系,如果打算讀取字節直到radIndex達到writeIndex同樣的值,你將會到達“可以讀取的”數據的末尾。如果試圖讀取超過改點的數據將會觸發一個IndexOutOfBoundsException。
名稱為read或者write開頭的ByteBuf方法,將會推進其對應的索引,而名稱以ste或get開頭的操作則不會。后面的這些方法將在作為一個參數傳入的一個相對索引上執行操作。
ByteBufHolder接口
我們經常發現,除了實際的數據負載之外,我們還需要存儲各種屬性值。HTTP響應便是一個很好的例子,除了表示為字節的內容,還包括狀態碼、cookie等。
為了處理這種常見的用例,Netty提供了ByteBufHolder。ByteBufHolder也為Netty的高級特性提供了支持,入緩沖區池化,其中可以從池中借用ByteBuf,並且在需要時自動釋放。
ByteBufHolder只有幾種用於訪問底層數據和引用計數的方法。
| 名稱 | 描述 |
|---|---|
| content() | 返回由這個ByteBufHolder所持有的ByteBuf |
| copy() | 返回這個ByteBufHolder得一個深拷貝,包括一個其所包含的ByteBuf的非共享拷貝 |
| duplicate() | 返回這個ByteBufHolder的以個淺拷貝,包括一個其所包含的ByteBuf的共享拷貝 |
如果想要實現一個將其有效負載存儲在ByteBuf中的消息對象,那么ByteBufHolder將是個不錯的選擇。
ByteBuf分配
- 按需分配:ByteBufAllocator接口
為了降低分配和釋放內存的開銷,Netty通過interface ByteBufAllocator實現了(ByteBuf的)池化,它可以用來分配我們所描述過的任意類型的ByteBuf實例。使用池化是應用程序的決定,其並不會以任何方式改變ByteBuf API(的語義)。
可以通過Channel(每一個可以有一個不同的ByteBufAllocator的實例)或者綁定到ChannelHandler的ChannelHnadlerContext獲取一個到ByteBufAlocator的引用。
Channel channel = ... ;
ByteBufAllocator allocator = channel.alloc();
...
ChannelHandlerContext ctx = ... ;
ByteBufAllocator allocator2 = ctx.alloc();
...
- Unpooled緩沖區
可能某些情況下,你未能獲取一個到ByteBufAllocator的引用。對於這種情況,Netty提供了一個簡單地稱為Unpooled的工具類,它提供了靜態的輔助方法來創建未池化的ByteBuf實例。
