netty4 buffer源碼分析

模塊類結構:

Netty-Buffer類都集中在io.netty.buffer的package中,主要功能是在數據傳輸時保存傳輸的數據,同時對通訊數據進行功能封裝,便於對數據空間進行管理。注:在其餘的package中有一些繼承自buffer的類,不在這裏說明。java

package以下:數組

netty_buffer1.png?version=1&modification

netty_buffer2.png?version=1&modification

工做原理簡介:

1、從存儲方式上來分,ByteBuf分爲:HeapByteBuf和DirectByteBuf。

HeapByteBuf是用byte數組格式來存儲數據,DirectByteBuf是使用java.nio.ByteBuffer來存儲數據。緩存

DirectByteBuf利用java.nio.ByteBuffer是藉助於JVM調用操做系統的底層通訊函數,直接操做直接緩衝區能夠減小中間緩衝區的複製操做,進而提供程序性能。網絡

HeapByteBuf是在JVM內部開闢緩衝區,在數據操做前先把數據複製到byte數組再進行處理。由於中間增長了一層數據複製操做,因此會影響性能。app

2、從空間初始化方式上來分,ByteBuf分爲:緩存方式分配和非緩存方式分配。

空間分配工具類:PooledByteBufAllocator和UnpooledByteBufAllocator,其中UnpooledByteBufAllocator在外又封裝了一個類Unpooled。ide


在緩存方式的分配類中,預申請了8個16K空間(分別是byte數組和java.nio.ByteBuffer)放在緩存中,經過相似工廠的建立函數建立ByteBuf時,先檢查緩存中的空間是否符合要求,若是符合先從緩存中分配空間,不然申請新的空間。函數

空間申請時判斷爲:大於chunkSize(16K),大於PageSize(8192)小於chunkSize(16K),小於PageSize大於512,小於512幾個檔。工具

注:這幾個值會根據操做系統的不一樣有所調整,以上值是在win7x64系統上的值。性能

3、空間分配和擴展

一、HeapByteBuf

   ①緩存方式測試

      a、分配

       根據構建是傳入參數的初始化空間,從對應的緩存空間中取得byte[],分配給到ByteBuf中memory變量。

      b、擴展

      在須要擴展的空間時,從緩存中取出一個符合要求的新的空間,即byte數組,把新申請的byte數組替換到ByteBuf中,執行System.arraycopy(src, srcOffset, dst, dstOffset, length);便可

   ②非緩存方式

     a、分配

      new一個byte[]賦值到ByteBuf的memory中,長度是傳入的初始化長度參數。

     b、擴展

     new一個新長度的byte[],替換ByteBuf中舊的memory,而後執行System.arraycopy(src, srcOffset, dst, dstOffset, length);便可

二、DirectByteBuf

   ①緩存方式

     a、分配

     根據入口的初始化大小參數從緩存中取得對應的空間區域進行分配。

     b、擴展

     根據申請空間的要求,從緩存中取得(緩存中沒法知足時new一個java.nio.ByteBuffer)知足要求的java.nio.ByteBuffer,替換掉原有的java.nio.ByteBuffer,若是系統判斷爲unSafe時,直接複製舊的數據到新的java.nio.ByteBuffer;若是系統判斷不爲unSafe時,說明舊的數據還可能有其餘Buffer訪問,因此新舊java.nio.ByteBuffer同時進行自身複製一個新的java.nio.ByteBuffer,而後進行數據複製。

   ②非緩存方式

     a、分配

     new一個java.nio.ByteBuffer複製給ByteBuf的memory變量,長度爲入口的初始化大小參數。

     b、擴展

     new一個新的java.nio.ByteBuffer複製給ByteBuf的memory變量,而後把數據複製到新的java.nio.ByteBuffer中。

4、其餘類型ByteBuf:

一、CompositeByteBuf

     是一個虛擬的ByteBuf,在內部用List保存多個ByteBuf,而後虛擬爲一個ByteBuf供使用。應用場景未知。

    同類型:FixedCompositeByteBuf,List長度爲固定長度

二、ReadOnly類型

     只讀ByteBuf。包括:ReadOnlyByteBuf、ReadOnlyByteBufferBuf、ReadOnlyUnsafeDirectByteBuf等

三、EmptyByteBuf

     定義一個空的ByteBuf

四、DuplicatedByteBuf

     不建議直接構造此類ByteBuf。執行ByteBuf內部duplicate()生成一個自己的副本。

五、WrappedByteBuf

   未知

    SimpleLeakAwareByteBuf

六、SwappedByteBuf

   反轉字序的ByteBuf

七、SlicedByteBuf

   分片的ByteBuf,經過ByteBuf的slice()方法返回,和調用者共享從position指針到limit指針的這部份內容。


5、通訊時的處理

      在服務端和客戶端通訊時,先把ByteBuf的capacity初始化爲1024,而後在接收數據的過程當中,再根據每次接收到的數據長度,猜想(動態計算)下次傳輸可能的數據長度,而後記錄這個長度,當下一次數據開始傳輸時把這個長度當作初始長度生成ByteBuf。而後依次循環使用,直到鏈接關閉爲止。


重點類介紹:

類繼承關係圖:

wpid-diagram.jpg?version=1&modificationD

一、UnpooledByteBufAllocator

   非緩存方式ByteBuf生成工具類,是ByteBufAllocator的一種簡單實現,生成的方式是每次調用都new一個新的ByteBuf。

   提供了各類ByteBuf的實現方法。

二、PooledByteBufAllocator

  緩存方式的ByteBuf生成工具類。預生成了一個高性能的buffer池,分配策略則是結合了buddy allocation和slab allocation的jemalloc變種。

  這是官方推薦的工具類,因爲在先前的測試中可能存在內存泄露的現象,因此在鏈接初始化時使用的UnpooledByteBufAllocator分配ByteBuf。可是在官方的最新公告中表示已經解決了此問題,之後仍是推薦使用PooledByteBufAllocator來生成ByteBuf來提供總體性能。


三、PoolArena

   ByteBuf空間分配核心類。提供了allocate()方法來初始化ByteBuf空間,以及reallocate()方法來動態擴容空間。

   同時在繼承類HeapArena和DirectArena類中提供了memoryCopy()函數的空間擴容的具體實現。

與其餘模塊接口:

1、ByteBuf的生成

    ByteBuf的生成不建議直接使用new一個具體類的方式來實現,netty提供了生成工具類PooledByteBufAllocator和UnpooledByteBufAllocator供用戶使用。

2、ByteBuf主要使用的方法說明

   一、capacity()方法和capacity(newCapacity)

     無參數時是返回buf現有的容量;

     有參數時是從新設置buf的容量,通常用於寫入時容量不足的狀況下進行擴容操做

   二、order()方法和order(ByteOrder endianness)方法

     無參數時返回buf的字節序

     有參數時設置buf的字節序,同時依照新的字節序進行轉換

   三、readableBytes()和readBytes(obj);

    readableBytes()返回當前可讀的字節數:從readIndex到結尾的字節數。通常用於讀取數據前進行數據完整性的判斷。

   readBytes()讀取數據到指定的obj變量中(還有其餘參數)。

   四、writableBytes()和maxWritableBytes()

    writableBytes()是返回當前capacity可寫入的字節數是多少;

   maxWritableBytes()是返回ByteBuf建立時的maxCapacity可寫入的字節數是多少

   五、markReaderIndex()和resetReaderIndex()

   markReaderIndex()把當前的readerIndex賦值到markReaderIndex中。

   resetReaderIndex()重設readerIndex,把markIndex賦值到readerIndex。

   這兩個方法和readableBytes()、readBytes()結合使用能夠完成數據的讀取操做。

   六、markWriterIndex()和resetWriterIndex()

   markWriterIndex()方法是把當前writeIndex賦值到markWriteIndex中。

   resetWriterIndex()是把writeIndex設置爲markWriteIndex的值。

   七、writeBytes()

   寫數據函數,把數據吸入到ByteBuf中。

   通常在寫數據時和markWriterIndex()和resetWriterIndex()結合使用

   八、 clear()

   清空ByteBuf,同時把readerIndex、markReaderIndex、writeIndex、markWriteIndex等設置爲0

3、netty→ByteBuf和java.nio.ByteBuffer的不一樣

  在java.nio.ByteBuffer有capacity、position、limit、mark四種概念,除了容量不會改變之外,position、limit和mark在讀寫時都會發生改變,而且在讀操做前要調用flip()方法重設position和limit才能夠正確讀出寫入的數據。

   而在netty的ByteBuf中,封裝時從新定義了readerIndex和writeIndex,在讀寫時只是操做對應的標誌位,開發者在使用當中讀寫時不用關心position、limit、mark,也不用執行flip()方法就能夠很方便的讀寫操做。

   java.nio.Bytebuffer讀寫實例:

   ByteBuffer buf = ByteBuffer.allocate(10);

   buf.put(xxx);//寫完畢轉讀取時

   buf.flip();//必須調用,不然讀取的數據不正確

   buf.get(xxx);


   而使用netty的ByteBuf時不須要關心這些指針,如:

   ByteBuf buf = xxx;//生成新的ByteBuf

   byte writeData[] = {...};

   buf.writeBytes(writeData);//寫入數據

   byte readData[] = new byte[11];

   buf.readBytes(readData);//不用其餘操做,可直接讀取


   經過上面的示例能夠看出,netty的ByteBuf使用起來更方便


4、HeapByteBuf和DirectByteBuf的選擇(我的理解

  HeapByteBuf由Heap管理,Heap是Java堆的意思,內部實現直接採用byte數組;DirectByteBuf使用是堆外內存,Direct應是採用Direct I/O之意,內部實現使用java.nio.DirectByteBuffer。

  注:HeapByteBuf經過

  在使用上的選擇:

  場景1:網絡間的通訊:

    A計算機的netty進程A1和B計算機上的netty進程A2進行通訊,這個時候涉及到跨網絡通訊的數據傳輸,前面說過DirectByteBuf是JVM直接操做操做系統的直接緩衝區可以×××能,那麼此時使用DirectByteBuf有利於提升程序性能,減小資源的使用。若是此時使用HeapByteBuf,那麼進程如今JVM內部分配緩衝區,寫完數據後再賦值到操做系統的直接緩衝區,而後進行網絡傳輸,這樣緩衝區的複製會影響性能,同時使用完畢後操做系統和JVM都要回收緩衝區資源,實際上增長了資源的使用,也下降了程序的性能。


  場景2:進程內部的通訊:

   同一臺計算機上的netty進程內進行通訊,這個時候只涉及到了進程內的通訊、涉及不到網絡間傳輸,這個時候使用HeapByteBuf會有更好的優點。


總體運行狀況:


其餘;

相關文章
相關標籤/搜索