相關文章
java網路編程—NIO與Netty(四)
java網路編程—NIO與Netty(三)
java網路編程—NIO與Netty(二)
java網路編程—NIO與Netty(一)
java網路編程—基石:五種IO模型及原理(多工\epoll) 資料容器——ByteBuf
JDK的NIO中使用ByteBuffer作為網路位元組流傳輸的容器,與ByteBuffer類似,Netty中定義了ByteBuff作為資料容器。
ByteBuff使用更方便、功能更強大靈活、效能更好。
ByteBuff通過定義了兩個索引(readIndex、writeIndex)對資料做管理,並且因為讀寫索引分離,不需要執行flip()操作。
ByteBuf——堆緩衝區
最常使用的ByteBuf將資料同個一個byte數組的方式儲存在堆記憶體中,這種方式稱為backing Array.
/** * Backing array:使用一個byte[] array作為底層資料結構 */ public static void heapBuffer() { ByteBuf heapBuf = Unpooled.buffer(1024); if (heapBuf.hasArray()) {//判斷heapBuf是否是backing Array類型 byte[] array = heapBuf.array();//擷取底層的數組 int offset = heapBuf.arrayOffset() + heapBuf.readerIndex(); int length = heapBuf.readableBytes();//擷取可讀取位元組數量 handleArray(array, offset, length); } } }**
ByteBuf——直接緩衝區
直接緩衝區是另外一種 ByteBuf 模式。NIO 在 JDK 1.4 中引入的ByteBuffer 類允許 JVM 實現通過本地調用來分配非堆的直接記憶體,不受GC管理。
為什麼要使用直接記憶體(DirectMemory)
對於網路傳輸是最理想的選擇。因為通常JVM處理網路程式時,會將資料從JVM的堆轉移到非堆直接記憶體上,然後再發送到傳輸層等等。也就是說為了網路傳輸,通常要將堆記憶體轉為非堆記憶體。那麼我們直接使用非堆記憶體處理網路傳輸會更加高效。Netty的一種零拷貝。 ByteBuf——複合緩衝區
CompositeByteBuf 實現了對兩種模式的合并
public static void byteBufComposite() { CompositeByteBuf messageBuf = Unpooled.compositeBuffer(); ByteBuf headerBuf = Unpooled.copiedBuffer("header".getBytes()); // can be backing or direct ByteBuf bodyBuf = Unpooled.directBuffer(); // can be backing or direct bodyBuf.writeBytes("body".getBytes()); messageBuf.addComponents(headerBuf, bodyBuf); messageBuf.removeComponent(0); // remove the header for (ByteBuf buf : messageBuf) { System.out.println(buf.toString() + "\n\r"); while (buf.isReadable()) { System.out.print((char) buf.readByte()); } // System.out.println(new String(buf.array())); } }
ByteBuf分配
ByteBufAllocator用來完成ByteBuf的分配初始化,實現了ByteBuf的池化。 PooledByteBufAllocator與UnpooledByteBufAllocator
Netty提供基於記憶體池的緩衝區資料重用機制,相比於朝生熄滅的ByteBuffer來說,可以極大提高效能。
Netty提供了兩種ByteBufAllocator的實現: PooledByteBufAllocator和UnpooledByteBufAllocator。前者池化了ByteBuf的執行個體以提高效能並最大限度地減少記憶體片段。後者的實現不池化ByteBuf執行個體, 並且在每次它被調用時都會返回一個新的執行個體。
API簡介:
返回一個基於堆或者直接記憶體儲存的 ByteBuf buffer() buffer(int initialCapacity); buffer(int initialCapacity, int maxCapacity);
返回一個基於堆記憶體儲存的ByteBuf heapBuffer() heapBuffer(int initialCapacity) heapBuffer(int initialCapacity, int maxCapacity)
返回一個基於直接記憶體儲存的ByteBuf directBuffer() directBuffer(int initialCapacity) directBuffer(int initialCapacity, int maxCapacity)
返回一個可以通過添加最大到指定數目的基於堆的或者直接記憶體儲存的緩衝區來擴充的CompositeByteBu compositeBuffer() compositeBuffer(int maxNumComponents) compositeDirectBuffer() compositeDirectBuffer(int maxNumComponents); compositeHeapBuffer() compositeHeapBuffer(int maxNumComponents);
返回一個用於通訊端的 I/O 操作的 ByteBuf
預設地,當所啟動並執行環境具有 sun.misc.Unsafe 支援時,返回基於直接記憶體儲存的 ByteBuf,否則返回基於堆記憶體儲存的 ByteBuf; ioBuffer() 讀寫訪問
/** * <pre> * get()和 set()操作, 從給定的索引開始,並且保持索引不變; * * <pre> * * read()和 write()操作, 從給定的索引開始,並且會根據已經訪問過的位元組數對索 引進行調整 */ public static void byteBufSetGet() { Charset utf8 = Charset.forName("UTF-8"); ByteBuf buf = Unpooled.copiedBuffer("Netty in Action rocks!", utf8); System.out.println((char) buf.getByte(0)); int readerIndex = buf.readerIndex(); int writerIndex = buf.writerIndex(); buf.setByte(0, (byte) 'B'); System.out.println((char) buf.getByte(0)); assert readerIndex == buf.readerIndex(); assert writerIndex == buf.writerIndex(); } public static void byteBufWriteRead() { Charset utf8 = Charset.forName("UTF-8"); ByteBuf buf = Unpooled.copiedBuffer("Netty in Action rocks!", utf8); System.out.println((char) buf.readByte()); int readerIndex = buf.readerIndex(); int writerIndex = buf.writerIndex(); buf.writeByte((byte) '?'); assert readerIndex == buf.readerIndex(); assert writerIndex != buf.writerIndex(); }
Netty預設使用了PooledByteBufAllocator可以從Channel中或者ChannelHandlerContext中擷取ByteBuffAllocator的引用。
public static void obtainingByteBufAllocatorReference() { Channel channel = CHANNEL_FROM_SOMEWHERE; // get reference form somewhere ByteBufAllocator allocator = channel.alloc(); // ... ChannelHandlerContext ctx = CHANNEL_HANDLER_CONTEXT_FROM_SOMEWHERE; // get reference form somewhere ByteBufAllocator allocator2 = ctx.alloc(); // ... }
零拷貝
1.NIO中使用直接記憶體(Direct Memory)。
傳統的資料發送,需要從JVM heap記憶體中拷貝資料到系統直接記憶體中,然後將直接記憶體的資料寫入Socket。
直接使用DirectMemory,可以減少記憶體資料的拷貝。
2.Netty提供了組合Buffer對象。
Netty 的 Zero-copy 體現在如下幾個個方面: Netty 提供了 CompositeByteBuf 類, 它可以將多個 ByteBuf 合并為一個邏輯上的 ByteBuf, 避免了各個 ByteBuf 之間的拷貝. 通過 wrap 操作, 我們可以將 byte[] 數組、ByteBuf、ByteBuffer等封裝成一個 Netty ByteBuf 對象, 進而避免了拷貝操作. ByteBuf 支援 slice 操作, 因此可以將 ByteBuf 分解為多個共用同一個儲存地區的 ByteBuf, 避免了記憶體的拷貝. 通過 FileRegion 封裝的FileChannel.tranferTo 實現檔案傳輸, 可以直接將檔案緩衝區的資料發送到目標 Channel, 避免了傳統通過迴圈 write 方式導致的記憶體拷貝問題.
樣本:transferTo/transferFrom具體樣本