netty源碼解析(4.0)-21 ByteBuf的設計原理

    io.netty.buffer包中是netty ByteBuf的實現。ByteBuf是一個二進制緩衝區的抽象接口,它的功能有:

  • 可以隨機訪問、順序訪問。
  • 支持基本數據類型(byte, short, int, long, float, double)的序列化和反序列化。
  • 不限制底層原始的數據類型,可以是byte[], NIO Buffer, String, IO Stream, 直接內(C語言中可以表示爲指向一塊內置起始地址的指針void*, 內存的長度), 等等。

爲什麼需要ByteBuf

    緩衝區的使用範圍非常廣泛:I/O操作,序列化/反序列化,編碼轉換,壓縮/解壓,加/解密等所有需要使用byte[]的場景。
    有些場景需要需要能夠快速地創建和銷燬緩衝區,如:高併發的服務器,處理請求、返回響應的時時候需要大量且高頻地創建,銷燬緩衝區。
    有些場景不能確定需要多大的緩衝區,如: 從數據流中分離出一條消息,消息的長度不確定,只知道最大長度。假如消息的最大長度是64KB,而消息的平均長度只有4KB, 如果每次創建64KB的緩衝區就太浪費了。如果緩衝區能夠在需要的時候自動且高效地增減容量,就完美了。
    在所有的場景中都涉及到頻繁的數據copy,這要求緩衝區數據copy的性能要儘量高。如果有可能,儘量減少數據copy。
    ByteBuf就是爲解決以上問題而設計的。

核心概念

如下所示

|  discardable bytes | readable bytes |  writable bytes  |
0                readerIndex       writerIndex       capacity
  • capacity: 緩衝區的容量。
  • readerIndex: 當前讀的位置。可以使用readerIndex()和readerIndex(int)方法獲取、設置readerIndex值。每次調用readXXX方法都會導致readerIndex向writerIndex移動,直到等於writerIndex爲止。
  • writerIndex: 寫的當前位置。可以使用writerIndex()和writerIndex(int)方法獲取、設置writeIndex的值。每次調用writeXXX方法都會導致writeIndex向capacity移動,直到等於capacity爲止。
  • discardable bytes: 可丟棄的數據。0–readerIndex之間的數據, 長度是readerIndex - 0,調用discardReadBytes會丟棄這部分數據,把readerIndex–writerIndex之間的數據移動到ByteBuf的開始位置(0), ByteBuf會變成如下所示的樣子:
       |   readable bytes  |  writable bytes  |
readerIndex(0)       writerIndex           capacity
  • readable bytes: 可讀數據。 readerIndex–writerIndex之間的數據,長度是writerInex - readerIndex。可以調用readableBytes()方法得到它的長度。
  • writeable bytes: 可寫的空間。長度是capacity - writerIndex。也可以認爲它是ByteBuf的剩餘空間。

核心能力

對二進制數據的讀寫是ByteBuf的核心能力。它提供兩種讀寫方式:

  • 隨機讀寫: getXXX(int), setXXX(int, …)。不會對readerIndex和writerIndex產生影響,範圍是(0,capacity]。
  • 順序讀寫: readXXX, 增加readerIndex的值,範圍是(readerIndex, writerIndex]。 writeXXX,增加writerIndex值,範圍是(writerIndex, capacity]。

ByteBuf爲了方便使用,提供了一些基本數據類型(unsigned表示無符號類型)的讀寫支持:

數據類型 長度(Byte)
byte, unsignedByte 1
short, unsignedShort 2
char 2
medium, unsignedMedium 3
int, unsignedInt 4
long 8
float 4
double 8

對這些基本數據類型的讀寫涉及到了字節須的問題,ByteBuf支持兩種字節序,使用java.nio.ByteOrder中的定義,默認的字節序是BIG_ENDIAN, 這個也是網絡字節序。

此外還提供了對byte[]類型及可以當成byte[]使用的數據類型的支持, 方法名都是:getBytes,setBytes, readBytes, writeBytes。

  • byte[]
  • ByteBuf
  • ByteBuffer
  • GatheringByteChannel
  • InputStread, OutputStream

內存管理

內存管理分爲兩個部分:內存分配,內存釋放。
ByteBufAllocator是內存分配的接口,它有兩個具體實現:UnpooledByteBufAllocator, PooledByteBufAllocator。
UnpooledByteBufAllocator是JVM內存分配接口的簡單封裝。
PooledByteBufAllocator在JVM內存分配的基礎上實現了一套獨立的內存分配算法。
內存釋放的關鍵是如何判定對象死亡,ByteBuf繼承了ReferenceCounted接口,使用引用計數的方式判定對象死亡。

PooledByteBufAllocator中高效的內存管理算法是ByteBuf的性能基礎,理解了它的算法是理解ByteBuf的關鍵。

體系結構

ByteBuf
AbstractByteBuf
SwappedByteBuf
WrappedByteBuf
AbstractReferenceCountedByteBuf
CompositeByteBuf

FixedCompositeByteBuf
PooledByteBuf
UnpooledDirectByteBuf

UnpooledHeapByteBuf

UnpooledUnsafeDirectByteBuf

UnpooledUnsafeHeapByteBuf

UnpooledUnsafeNoCleanerDirectByteBuf
ReadOnlyByteBufferBuf
PooledDirectByteBuf
PooledUnsafeDirectByteBuf
PooledHeapByteBuf
PooledUnsafeHeapByteBuf

上圖是ByteBuf體系結構中主要的類和接口。主要分爲三大類:

  • AbstractReferenceCountedByteBuf及其子類。這個類別是是ByteBuf中最重要的部分,它分爲4個小類別:

CompositeByteBuf, FixedCompositeByteBuf: 把不同的ByteBuf組合成一個ByteBuf。
PooledByteBuf:實現了自定義內存管理算法的。
UnpooledXXXX: 直接使用JVM內存管理能力。
ReadOnlyByteBufferBuf: 只讀的。

  • SwappedByteBuf: 用來轉換ByteBuf的字節序。
  • WrappedByteBuf: 用來包裝另一個ByteBuf。

工具

有兩個工具類幫助開發者使用ByteBuf:

  • ByteBufUtil: 這個類中創建好了默認的ByteBufAllocator,可以直接拿來用。還有一些操作ByteBuf常用的方法。
  • Unpooled: 這個類是針對UnpooledXXXByteBuf的工具。

用法

創建ByteBufAllocator

    使用ByteBufUtil.DEFAULT_ALLOCATOR得到ByteBufAllocator實例。這個實例可能是UnpooledByteBufAllocator,也可能是PooledByteBufAllocator類型,這取決於io.netty.allocator.type屬性的設置。默認是unpooled,UnpooledByteBufAllocator類型。如果想要PooledByteBufAllocator類型,把這個屬性的值設置成pooled:
    java -Dio.netty.allocator.type=pooled,或者System.setProperty(“io.netty.allocator.type”, “pooled”)
    

創建ByteBuf

    netty不建議直接創建創建ByteBuf實例,推薦使用ByteBufAllocator創建ByteBuf實例,或者使用Unpooled靜態方法。
    ByteBufAllocator有7種方法用於創建ByteBuf實例:

方法名 特性
buffer 使用可能是JVM堆內存或直接內存,取決於具體的實現
ioBuffer 如果可以的話優先使用直接內存
heapBuffer 使用堆內存
directBuffer 使用直接內存
CompositeByteBuf 使用可能是JVM堆內存或直接內存,取決於具體的實現
compositeHeapBuffer 使用堆內存
compositeDirectBuffer 使用堆內存

Unpooled創建ByteBuf實例的方法有2兩種:

方法名 特性
buffer 使用堆內存
directBuffer 使用直接內存

包裝成ByteBuf

Unpooled提供了一系列的wrappedBuffer方法,把一些數據類型包裝成一個ByteBuf, 這些數據類型有:

  • byte[]。
  • ByteBuffer。
  • 另一個的ByteBuf中的可讀數據。

wrappedBuffer方法還可以把byte[]…, ByteBuffer…, ByteBuf…包裝成一個CompositeByteBuf。

數據讀寫

數據讀寫是ByteBuf的基本功能,前面已經講過,相關方法是: getXXX, readXXX, setXXX, writeXXX。

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