netty4 buffer源碼分析

模塊類結構:

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

package如下:

netty_buffer1.png?version=1&modification

netty_buffer2.png?version=1&modification

工作原理簡介:

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

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

DirectByteBuf利用java.nio.ByteBuffer是藉助於JVM調用操作系統的底層通信函數,直接操作直接緩衝區可以減少中間緩衝區的複製操作,進而提供程序性能。

HeapByteBuf是在JVM內部開闢緩衝區,在數據操作前先把數據複製到byte數組再進行處理。因爲中間增加了一層數據複製操作,所以會影響性能。

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

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


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

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

注:這幾個值會根據操作系統的不同有所調整,以上值是在win7x64系統上的值。

三、空間分配和擴展

1、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);即可

2、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中。

四、其他類型ByteBuf:

1、CompositeByteBuf

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

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

2、ReadOnly類型

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

3、EmptyByteBuf

     定義一個空的ByteBuf

4、DuplicatedByteBuf

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

5、WrappedByteBuf

   未知

    SimpleLeakAwareByteBuf

6、SwappedByteBuf

   反轉字序的ByteBuf

7、SlicedByteBuf

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


五、通信時的處理

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


重點類介紹:

類繼承關係圖:

wpid-diagram.jpg?version=1&modificationD

1、UnpooledByteBufAllocator

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

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

2、PooledByteBufAllocator

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

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


3、PoolArena

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

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

與其他模塊接口:

一、ByteBuf的生成

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

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

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

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

     有參數時是重新設置buf的容量,一般用於寫入時容量不足的情況下進行擴容操作

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

     無參數時返回buf的字節序

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

   3、readableBytes()和readBytes(obj);

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

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

   4、writableBytes()和maxWritableBytes()

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

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

   5、markReaderIndex()和resetReaderIndex()

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

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

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

   6、markWriterIndex()和resetWriterIndex()

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

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

   7、writeBytes()

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

   一般在寫數據時和markWriterIndex()和resetWriterIndex()結合使用

   8、 clear()

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

三、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使用起來更方便


四、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會有更好的優勢。


整體運行情況:


其他;


發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章