目錄
Bytebuf類別
Bytebuf的繼承圖
Bytebuf的分類
從內存角度
堆內存字節緩衝區(HeapBytebuf)
優點:堆內存的分配和回收速度快,可以被JVM自動回收。
缺點:如果進行Socket的I/O讀寫的時候還需要一次複製,將字節複製到堆內存外的內存,然後使用,所以性能會有一定程度的下降
直接內存字節緩衝區(DirectBytebuf)
優點:不需要一次額外的字節複製,讀寫速度回快一些
缺點:內存分配和回收要慢一些
Bytebuf的最佳實踐:在I/O通信線程使用過程是推薦使用DirectBytebuf,因爲不會每次讀寫都會創建新的Bytebuf對象,
在後端業務消息的編碼模塊推薦使用HeapBytebuf,因爲Bytebuf對象的回收和創建要快一些
從內存回收的角度
基於對象池的PooledByteBuf
優點:基於對象池的Bytebuf會重用Bytebuf對象,在PooledByteBuf內部維護了一個對象池,每次從池中獲取Bytebuf,減少了創建和銷燬Bytebuf對象的消耗,提高了內存的使用率。
缺點:對象池的維護和管理操作複雜
另外一種是普通Bytebuf,優缺點則和對象池相反
最佳實踐:推薦使用對象池Bytebuf
創建Bytebuf
基於UnpooledHeapByteBuf類型(常用)
ByteBuf buf = Unpooled.buffer(initialCapacity) 創建一個給定大小的ByteBuf
ByteBuf buf1 = Unpooled.copiedBuffer(byte byte[]); 將一個byte數組複製給ByteBuf
基於UnpooledDirectByteBuf類型
ByteBuf buf = Unpooled.directBuffer(initialCapacity)
基於堆內存的池對象
PooledByteBufAllocator pool = new PooledByteBufAllocator();
ByteBuf heapBuf = pool.buffer(initialCapacity);
基於直接內存的池對象
PooledByteBufAllocator pool = new PooledByteBufAllocator();
ByteBuf dirctBuf = pool.directBuffer(initialCapacity);
ByteBufHolder
我們經常在ByteBuf中存儲一些正常數據之外,我們有時候還需要增加一些各式各樣的屬性值,一個Http響應體就是一個很好的例子,除了按照字節傳輸過來的主體內容,還有狀態碼,cookie等信息
Netty提供了ByteBufHolder來處理這些常用的用戶案例,ByteBufHolder還提供了Netty一些其他的先進特性,例如緩存池,緩存池可以是ByteBuf中直接“借出”獲取,如果有需要,“借出”的ByteBuf還可以自動的還到池中
ByteBufHolder提供了一系列的獲取底層數據和引用計數的方法,表5.6向你展示了一些常用的方法
如果你想要實現一個消息對象可以在ByteBuf中存儲其有效負荷的話,使用ByteBufHolder是一個不錯的選擇
ByteBufAllocator
ByteBufAllocator
爲了減少分配和釋放內存的開銷,Netty 通過支持池類 ByteBufAllocator,可用於分配的任何 ByteBuf 我們已經描述過的類型的實例。是否使用池是由應用程序決定的,列出了 ByteBufAllocator 提供的操作。
名稱 | 描述 |
---|---|
buffer() buffer(int) buffer(int, int) | Return a ByteBuf with heap-based or direct data storage. |
heapBuffer() heapBuffer(int) heapBuffer(int, int) | Return a ByteBuf with heap-based storage. |
directBuffer() directBuffer(int) directBuffer(int, int) | Return a ByteBuf with direct storage. |
compositeBuffer() compositeBuffer(int) heapCompositeBuffer() heapCompositeBuffer(int) directCompositeBuffer()directCompositeBuffer(int) | Return a CompositeByteBuf that can be expanded by adding heapbased or direct buffers. |
ioBuffer() | Return a ByteBuf that will be used for I/O operations on a socket. |
通過一些方法接受整型參數允許用戶指定 ByteBuf 的初始和最大容量值。你可能還記得,ByteBuf 存儲可以擴大到其最大容量。
得到一個 ByteBufAllocator 的引用很簡單。你可以得到從 Channel (在理論上,每 Channel 可具有不同的 ByteBufAllocator ),或通過綁定到的 ChannelHandler 的 ChannelHandlerContext 得到它,用它實現了你數據處理邏輯。
下面的列表說明獲得 ByteBufAllocator 的兩種方式。
Channel channel = ...;
ByteBufAllocator allocator = channel.alloc(); //1
....
ChannelHandlerContext ctx = ...;
ByteBufAllocator allocator2 = ctx.alloc(); //2
...
1.從 channel 獲得 ByteBufAllocator
2.從 ChannelHandlerContext 獲得 ByteBufAllocator
Netty 提供了兩種 ByteBufAllocator 的實現,一種是 PooledByteBufAllocator,用ByteBuf 實例池改進性能以及內存使用降到最低,此實現使用一個“jemalloc”內存分配。其他的實現不池化 ByteBuf 情況下,每次返回一個新的實例。
Netty 默認使用 PooledByteBufAllocator,我們可以通過 ChannelConfig 或通過引導設置一個不同的實現來改變。更多細節在後面講述 ,見 Chapter 9, "Bootstrapping Netty Applications"
CompositeByteBuf
CompositeBytebuf允許將多個Bytebuf的實例組合起來,在內部使用一個Bytebuf進行統一管理。
例如在協議中一個協議通常會包含多個部分,消息體,消息頭等,他們都是Bytebuf對象,如果我們需要對他進行管理,可以把這些Bytebuf對象都放到CompositeBytebuf中進行統一管理
public class Test {
public static void bytebuf() {
PooledByteBufAllocator pool = new PooledByteBufAllocator();
CompositeByteBuf buf = new CompositeByteBuf(pool, true, 2000);
byte[] b1 = {1,2,3};
byte[] b2 = {4,5,6};
buf.addComponent(Unpooled.copiedBuffer(b1));
buf.addComponent(Unpooled.copiedBuffer(b2));
byte b = buf.getByte(3);
System.out.println(b);
}
public static void main(String[] args) {
Test.bytebuf();
}
}
結果是 4;
ByteBufUtil
這個是ByteBuf的一個靜態工具類
常用的有 encodeString():對字符串進行編碼,生存ByteBuf對象
decodeString():對字符串進行解碼,生存ByteBuf對象
hexDump():能夠將參數ByteBuf的內容轉換爲十六進制的字符串