java網路編程—NIO與Netty(四) ByteBuffer資料處理與零拷貝__編程

來源:互聯網
上載者:User

相關文章
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具體樣本

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.