<kbd id="afajh"><form id="afajh"></form></kbd>
<strong id="afajh"><dl id="afajh"></dl></strong>
    <del id="afajh"><form id="afajh"></form></del>
        1. <th id="afajh"><progress id="afajh"></progress></th>
          <b id="afajh"><abbr id="afajh"></abbr></b>
          <th id="afajh"><progress id="afajh"></progress></th>

          百萬并發(fā)「零拷貝」技術系列之經典案例Netty

          共 5047字,需瀏覽 11分鐘

           ·

          2021-03-09 12:14


          源 / 碼農神說    / 韓旭






          Netty在零拷貝思想上的實現可以理解為是廣義的,它和wiki對零拷貝寬泛的定義特別吻合“CPU 不需要將數據從一塊內存拷貝到另一塊內存”,因為Netty主要是在用戶空間盡量減少內存的拷貝次數,而非系統(tǒng)層面的用戶空間和內核空間數據的拷貝。


          Netty作為Java界知名的NIO網絡通訊框架,憑借其高性能木秀于mina、twisted,其因素之一就如官方所述:“減少了不必要的內存拷貝”。


          在零拷貝實現上,它有借助于Java NIO的tranferTo實現的FileRegion用于文件傳輸,也有通過巧妙設計buffer數據結構來避免由于拆分、組合而帶來的拷貝。尤其是后者,因為buffer是用來化零為整降低I/O操作頻率的重要技術手段,對性能的影響至關重要。



          FileRegion



          FileRegion的零拷貝是體現在系統(tǒng)層面的,它包裝了Java NIO的FileChannel.tranferTo方法進行文件傳輸,從FileRegion的默認實現類DefaultFileRegion可以一探究竟。


          tranferTo在上一篇推文有較詳細的講解,此處不再累述。




          ByteBuf



          Netty使用了它自己封裝的buffer API替代了Java NIO的ByteBuffer:ByteBuf。官方列出了它的一些比較酷的特性

          • You can define your buffer type if necessary.根據需要可以定制自己的buffer類型。

          • Transparent zero copy is achieved by built-in composite buffer type.通過內建的組合類型可以實現透明的零拷貝。

          • A dynamic buffer type is provided out-of-the-box, whose capacity is expanded on demand, just like `StringBuffer`.它是一個開箱即用可根據需求動態(tài)擴展的buffer,就像`StringBuffer`。

          • There's no need to call the `flip()` method anymore.不再需要調用`flip()` 方法。

          • It is often faster than `ByteBuffer`.通常比`ByteBuffer`更快速。



          DirectByteBuffer
          實際上ByteBuf提供了非常豐富的實現類如下圖所列,在邏輯上主要分為堆內buffer(HeapByteBuf)和堆外buffer(DirectByteBuf)。



          DirectByteBuf是用Java NIO的DirectByteBuffer實現的,所謂堆外buffer是相對于JVM堆內而言的,但網上有些資料把它和DMA混淆了。DirectByteBuffer只是避免了JVM堆內向堆外的拷貝,但這個“堆內、堆外”依然是用戶空間的范疇,因為 DirectByteBuffer是malloc() 分配出來的內存,用戶空間和內核空間請參考上一篇。


          拿網絡傳輸來說,對于傳統(tǒng)的read/write的I/O方式,一般情況而言是這樣的:Java堆內存—>用戶空間的堆外內存—>內核socket緩沖區(qū)—>DMA—>網卡—>網卡—>DMA—>內核socket緩沖區(qū)—>用戶空間的堆外內存—>Java堆內存。

          DirectByteBuffer的使用是有一定的風險的,可能會造成OutOfMemory,官方是這樣描述的


          allocating many short-lived direct NIO buffers often causes an OutOfMemoryError.


          堆內和堆外內存各有優(yōu)勢和劣勢,需要根據場景自行選擇



          網絡傳輸的過程中對數據的拆包、組包等操作十分常見也很頻繁,Netty提供了warp、Composite和slice方法來減少數據的拷貝,達到性能的提升的目標。



          wrap包裝




          Netty可以通過各種wrap方法, 將 byte[]、ByteBuf、ByteBuffer等包裝成一個ByteBuf對象,而不需要進行數據的拷貝。實際上Java NIO的ByteBuffer也有wrap,但Netty的ByteBuf提供了更豐富和便捷的wrap。



          通常將一個對象比如byte數組轉換成一個ByteBuffer,傳統(tǒng)的作法是把數組拷貝到ByteBuffer對象中,而wrap的方式無須拷貝,它們共用同一塊內存。


          byte[] tmp=new byte[]{1,2};

          //Java NIO
          //傳統(tǒng)方式(拷貝)
          ByteBuffer byteBuffer=ByteBuffer.allocate(2);
          byteBuffer.put(tmp);
          //wrap方式
          byteBuffer=ByteBuffer.wrap(tmp);

          //Netty ByteBuf
          //傳統(tǒng)方式(拷貝)
          ByteBuf byteBuf = Unpooled.buffer();
          byteBuf.writeBytes(tmp);
          //wrap方式
          byteBuf=Unpooled.wrappedBuffer(tmp);




          CompositeByteBuf組包



          CompositeByteBuf將多個ByteBuf組合成一個ByteBuf而不需要數據拷貝,每個ByteBuf都是獨立存在的,只是在邏輯上的組合,提高性能的同時可以統(tǒng)一使用ByteBuf的API。假設有一個數據包是由三部分組成header、body、footer,它們可能是由不同的模塊創(chuàng)建的,組合示意和代碼如下


          ByteBuf header = Unpooled.wrappedBuffer(tmp);
          ByteBuf body = Unpooled.wrappedBuffer(tmp);
          ByteBuf footer = Unpooled.wrappedBuffer(tmp);
          //不建議的作法
          ByteBuf wholeBuf = Unpooled.buffer(header.readableBytes() 
              + body.readableBytes()+footer.readableBytes());
          wholeBuf.writeBytes(header);
          wholeBuf.writeBytes(body);
          wholeBuf.writeBytes(footer);
          //建議使用組合
          CompositeByteBuf compositeByteBuf=Unpooled.compositeBuffer();
          //第一個參數increaseWriterIndex,為true會自動增加writerIndexss
          compositeByteBuf.addComponents(true,header,body,footer);





          slice拆分包



          slice將一個ByteBuf分解為多個ByteBuf,但沒有數據拷貝,而是共享同一個存儲區(qū)域的,這在拆分包操作時非常有用,如上例數據包由header、body、footer三部分組成,拆分示意和代碼如下


          ByteBuf byteBuf4slice=.....;
          ByteBuf header=byteBuf4slice.slice(0,5);
          ByteBuf body=byteBuf4slice.slice(5,15);
          ByteBuf footer=byteBuf4slice.slice(15,20);




          polled池化



          Netty 4.x提供了池化的Buffer,類似于線程池或數據庫連接池的思想,避免了Buffer頻繁的創(chuàng)建和釋放帶來的性能低效及GC壓力。池化和非池化的性能對比如下


          int loop = 3000000;
          byte[] content="this is a test".getBytes();

          //池化buffer
          long startTime = System.currentTimeMillis();
          ByteBuf pooledBuf = null;
          for (int i = 0; i < loop; i++) {
              pooledBuf= PooledByteBufAllocator.DEFAULT.buffer(1024);
              pooledBuf.writeBytes(content);
              pooledBuf.release();
          }
          long pooledTime=System.currentTimeMillis()-startTime;
          System.out.println("3百萬次池化buffer消耗的時間:"+pooledTime);

          //非池化buffer
          startTime = System.currentTimeMillis();
          ByteBuf unPooledBuf = null;
          for (int i = 0; i < loop; i++) {
              unPooledBuf= Unpooled.buffer(1024);
              unPooledBuf.writeBytes(content);
              unPooledBuf.release();
          }
          long unPooledTime=System.currentTimeMillis()-startTime;
          System.out.println("3百萬次池化buffer消耗的時間:"+unPooledTime);

          //性能提升
          System.out.println("池化buffer性能提升:"+Double.valueOf(
              String.format("%.2f",(unPooledTime-pooledTime)/(double)unPooledTime))*100
              +"%");


          執(zhí)行后從輸出可見,池化后的buffer性能提升20%左右,非常可觀


          3百萬次池化buffer消耗的時間:766
          3百萬次池化buffer消耗的時間:989
          池化buffer性能提升:23.0%





          寫在最后
          Netty在Java界經之所以久不衰自有它的優(yōu)勢,雖然Netty5夭折了,但Netty4依然足夠哦強大,開發(fā)者不僅把它用于實現各種通訊應用,還在各種框架中起著頂梁柱的角色,比如阿里的Dubbo。
          零拷貝系列以計算機組成及操作系統(tǒng)入手,以零拷貝思想在Linux和Java中的實現為傳承,最終以Netty作為經典案例分析收尾,希望能對您有所啟發(fā),感謝關注。



          —  —


          一鍵三連「分享」、「點贊」和「在看」

          技術干貨與你天天見~


          瀏覽 64
          點贊
          評論
          收藏
          分享

          手機掃一掃分享

          分享
          舉報
          評論
          圖片
          表情
          推薦
          點贊
          評論
          收藏
          分享

          手機掃一掃分享

          分享
          舉報
          <kbd id="afajh"><form id="afajh"></form></kbd>
          <strong id="afajh"><dl id="afajh"></dl></strong>
            <del id="afajh"><form id="afajh"></form></del>
                1. <th id="afajh"><progress id="afajh"></progress></th>
                  <b id="afajh"><abbr id="afajh"></abbr></b>
                  <th id="afajh"><progress id="afajh"></progress></th>
                  国产无遮挡又黄又爽免费软件 | 久久精品69 | 成人淫色电影网 | 91首页| 欧美成人一区二区三区四区 |