Netty入門學習-ByteBuf

ByteBuf是Java NIO ByteBuffer的替代品,是網絡數據基本單位字節的容器。數組

ByteBuf的API

Netty的數據處理API經過兩個組件暴漏:抽象類ByteBuf和接口ByteBufHolder網絡

ByteBuf優勢:app

  • 他能夠被用戶自定義的緩衝區類型擴展
  • 經過內置的複合緩衝區類型實現了透明的零拷貝
  • 容量能夠按需增加
  • 在讀寫兩種模式之間切換不須要調用ByteBuffer的flip()方法
  • 讀寫使用不一樣的索引
  • 支持方法的鏈式調用
  • 支持引用計數
  • 支持池化

ByteBuf——數據容器

工做原理

 ByteBuf維護兩個索引:一個用於讀取,一個用於寫入。當你從ByteBuf讀取時,readIndex會被遞增已經被讀取的字節數,一樣的,當向ByteBuf中寫入數據時,writeIndex也會被遞增。
image
若是readIndex和writeIndex處於一樣的位置,再次嘗試讀取數據將會觸發IndexOutOfBoundsException工具

名稱以read或者write開頭的ByteBuf方法,將會推動其對應的索引,而名稱以set或者get開頭的操做則不會。性能

名稱以set或者get開頭的方法會有一個索引位置參數,將會在該索引位置上進行set或get操做優化

ByteBuf能夠指定最大容量。若是寫索引超過這個值會觸發異常IllegalArgumentException。默認的最大值是Integer.MAX_VALUEspa

ByteBuf使用模式

堆緩衝區

 將數據存儲在JVM的對空間中,這種模式又被成爲支撐數組。它能在沒有使用池化的狀況下提供快速的分配和釋放。對象

當hasArray方法返回false時,嘗試訪問支撐數組將觸發UnsupportedOperationException異常。索引

直接緩衝區

 NIO引入的ByteBuffer類容許JVM實現經過本地調用來分配內存,這樣能夠避免在每次調用本地I/O操做以前(或者以後)將緩衝區的內容複製到一箇中間緩衝區(或者從中間緩衝區把內容複製到緩衝區)。接口

 直接緩衝區的主要特色是,分配和釋放都比較昂貴。

複合緩衝區

 它爲多個ByteBuf提供了一個聚合視圖。能夠根據須要添加或者刪除ByteBuf實例。Netty經過CompositeByteBuf(ByteBuf的子類)實現這個模式,它提供了一個將多個緩衝區表示爲單個合併緩衝區的虛擬表示。

CompositeByteBuf中的ByteBuf實例可能同時包含直接內存分配和非直接內存分配。若是隻有一個ByteBuf實例,那麼CompositeByteBuf上的hasArray方法將返回該ByteBuf上的hasArray方法的值,不然將返回false。

 CompositeByteBuf不支持訪問支撐數組,所以訪問CompositeByteBuf中的數據相似於訪問直接緩衝區的模式。

 Netty使用CompositeByteBuf來優化套接字I/O操做,儘量消除由JDK的緩衝區實現所致使的性能以及內存使用率的懲罰,這種優化發生在Netty的核心代碼中,不會被暴露出來。

字節級操做

隨機訪問索引

 ByteBuf的索引是從0開始:第一個字節的索引就是0,最後一個字節的索引是capacity() - 1。

若是方法中有一個索引值參數,經過該方法訪問數據既不會改變readIndex也不會改變writeIndex。

若是有須要,能夠經過調用readIndex(index)或者writeIndex(index)手動移動二者。

順序訪問索引

ByteBuf內部分段示意圖以下:

ByteBuf內部分段

可丟棄字節

 在上圖中可丟棄字節指的就是已被讀取過的字節,經過調用discardReadBytes()方法,能夠丟棄它們並回收空間。可丟棄字節分段的初始大小爲0,即readIndex,該值會隨着read操做的執行而增長(get*操做不會移動readIndex)。

discardReadBytes()方法只是移動了能夠讀取的字節以及writeIndex,而沒有對全部可寫入的字節進行擦除寫。

discardReadBytes()會致使內存複製,由於可讀字節必需要移動到緩衝區開始的位置。

可讀字節

 可讀字節分段存儲了實際數據。新分配的、包裝的或者複製的緩衝區的默認readINdex值爲0。任何名稱以read或者skip開頭的操做都將檢索或者跳過位於當前readIndex的數據,並將它增長已讀字節數。

若是被調用的方法須要一個ByteBuf參數做爲寫入的目標,而且沒有指定目標索引參數,那麼該寫入的目標的writeIndex也將被增長。

可寫字節

 能夠字節分段是指一個擁有未定義內容的、寫入就緒的內存區域。新分配的緩衝區的writeIndex的默認值爲0。任何以write開頭的方法都將從當前的writeIndex開始寫數據,並將它增長已經寫入的字節數。若是寫操做的目標也是ByteBuf,而且沒有指定源索引的值,則源緩衝區的readerIndex也會被增長相同的大小。

索引管理

  JDK的InputStream定義了mark(int readLimit)和reset()方法,這些方法分別被用來將流中的當前位置標記爲指定的值,以及將流重置到該位置。

在ByteBuf中,能夠調用markReadIndex()、markWriteIndex()、resetReaderIndex()、resetWriterIndex()來標記和重置ByteBuf的readIndex和writeIndex,不過在ByteBuf中沒有readLimit參數指定標記啥時候失效。

在ByteBuf中,也能夠經過調用readerIndex(int)或者writeIndex(int)來將索引移動到指定位置。

在ByteBuf中,能夠經過clear()方法將readerIndex和writeIndex都設置爲0,可是不會清楚內存中的內容。調用clear()方法比調用discardReadBytes()更加輕量,由於clear只是重置索引,不會複製任何的內存。

查找操做

 最簡單的肯定值的索引的方法是indexOf()。較複雜的查找能夠經過一個ByteProcessor(Netty4.1版本以上,舊的版本採用ByteBufProcessor)參數達成。

派生緩衝區

 派生緩衝區爲ByteBuf提供了以專門的方式來呈現其內容的視圖,這類視圖的建立方式主要有如下幾種:

  1. duplicate()
  2. slice()
  3. slice(int, int)
  4. Unpooled.unmodifiableBuffer(...)
  5. order(ByteOrder)
  6. readSlice(int)

以上方法都會返回一個新的ByteBuf實例,它具備本身的讀索引、寫索引和標記索引。它會和源實例共享內存,所以建立成本低廉,可是若是修改它的內容,也就意味着修改了對應的源實例。

若是須要一個現有緩衝區的真實副本,須要使用copy()或者copy(int,int)方法

讀/寫操做

讀寫操做主要分爲兩類:

  • get()和set()操做,從給定的索引開始,而且保持索引不變
  • read()和write()操做,從給定的索引開始,而且會根據已經訪問過的字節數對索引進行調整。
方法名稱 描述
getBoolean(int) 返回給定索引處的Boolean值
getByte(int) 返回給定索引處的字節
getUnsignedByte(int) 將給定索引處的無符號字節值做爲short返回
getMedium(int) 返回給定索引處的24位的中等int值
getUnsignedMedium(int) 返回給定索引處的無符號的24位的中等int值
getInt(int) 返回給定索引處的int值
getUnsignedInt(int) 將給定索引處的無符號int值做爲long返回
getLong(int) 返回給定索引處long值
getShort(int) 返回給定索引處的short值
getUnsignedShort(int) 將給定索引處的無符號short值做爲int返回
getByte(int, ...) 將該緩衝區中從給定索引開始的數據傳送到指定的目的地
方法名稱 描述
setBoolean(int, boolean) 設定給定索引處的Boolean值
setByte(int index, int value) 設定給定索引處的字節值
setMedium(int index, int value) 設定給定索引處的24位的中等int值
setInt(int index, int value) 設定給定索引處的int值
setLong(int index, long value) 設定給定索引處的long值
setShort(int index, int value) 設定給定索引處的short值
方法名稱 描述
readBoolean() 返回當前readIndex處的Boolean值,並將readIndex增長1
readByte() 返回當前readIndex處的字節,並將readIndex增長1
readUnsignedByte() 將當前readIndex處的無符號字節值做爲short返回,並將readIndex增長1
readMedium() 返回當前readIndex處的24位的中等int值,並將readIndex增長3
readUnsignedMedium() 返回當前readIndex處的24位的無符號的中等int值,並將readIndex增長3
readInt() 返回當前readIndex處的int值,並將readIndex增長4
readUnsignedMedium() 返回當前readIndex處的24位的無符號的中等int值,並將readerIndex增長3
readInt() 返回當前readIndex處的int值,並將readerIndex增長4
readUnsignedInt() 將當前readerIndex處的無符號的int值做爲long值返回,並將readIndex增長4
readLong() 返回當前readIndex處的long值,並將readIndex增長8
readShort() 返回當前readIndex處的short值,並將readIndex增長2
readUnsignedShort() 將當前readIndex處的無符號short值做爲int值返回,並將readIndex增長2
readBytes(ByteBuf byte[] destination, int dstIndex, [, int length]) 將當前ByteBuf中從當前readIndex處開始的(若是設置了,length長度的字節)數據傳送到一個目標ByteBuf或者byte[],從目標的dstIndex開始的位置。本地的readIndex將被增長已經傳輸的字節數。
方法 描述
writeBoolean(boolean) 在當前writeIndex處寫入一個boolean值,並將writeIndex增長1
writByte(byte) 在當前writeIndex處寫入一個字節值,並將writeIndex增長1
writeMedium(int) 在當前writeIndex處寫入一箇中等的int值,並將writeIndex增長3
writeInt(int) 在當前writeIndex處寫入一個int值,並將writeIndex增長4
writeLong(long) 在當前writeIndex處寫入一個long值,並將writeIndex增長8
writeShort(int) 在當前writeIndex處寫入一個short值,並將writeIndex增長2
writeBytes(source ByteBuf byte[] [,int srcIndex,int length]) 從當前writeIndex開始,傳輸來自於指定源(ByteBuf或者byte[])的數據。若是提供了srcIndex和length,則從srcIndex開始讀取,而且處理長度爲length的字節。當前writeIndex將會被增長所寫入的字節數。
方法 描述
isReadable() 若是至少有一個字節可供讀取,則返回true
isWritable() 若是至少有一個字節可被寫入,則返回true
readableBytes() 返回可被讀取的字節數
writableBytes() 返回可被寫入的字節數
capacity() 返回ByteBuf可容納的字節數。在此以後,它會嘗試再次擴展直到達到maxCapacity()
maxCapacity() 返回ByteBuf能夠容納的最大字節數
hasArray() 若是ByteBuf由一個字節數組支撐,則返回true
array() 若是ByteBuf由一個字節數組支撐則返回該數組;不然,它將拋出一個UnsupportedOperationException異常

ByteBufHolder接口

 ByteBufHolder爲Netty的高級特性提供了支持,如緩衝區池化,其中能夠從池中借用ByteBuf,而且在須要時自動釋放。

名稱 描述
content() 返回由這個ByteBufHolder所持有的ByteBuf
copy() 返回這個ByteBufHolder的一個深拷貝,包括一個其所包含的ByteBuf的非共享拷貝
duplicate() 返回這個ByteBufHolder的一個淺拷貝,包括一個其所包含的ByteBuf的共享拷貝

ByteBuf分配

按需分配:ByteBufAllactor接口

 爲了下降分配和釋放內存的開銷,Netty經過ByteBufAllactor接口實現了(ByteBuf的)池化,它能夠用來分配咱們所描述過的任意類型的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)
返回一個基於直接內存存儲的ByteBuf
compositeBuffer()
compositeBuffer(int maxNumComponents)
compositeDirectBuffer()
compositeDirectBuffer(int maxNumComponents)
compositeHeapBuffer()
compositeHeapBuffer(int maxNumComponents)
返回一個能夠經過添加最大到指定數目的基於堆的或者直接內存存儲的緩衝區來擴展的CompositeByteBuf
ioBuffer() 返回一個用於套接字的I/O操做的ByteBuf

Netty提供了兩種ByteBufAllocator的實現:PooledByteBufAllocator和ByteBufAllocator。前者池化了ByteBuf的實例以提升性能並最大限度的減小內存碎片,後者的實現不池化ByteBuf實例,而且每次被調用時都會返回一個新的實例。

Netty4.1之後默認使用PooledByteBufAllocator

Unpooled緩衝區

 Netty提供了一個簡單的成爲Unpooled的工具類,它提供了靜態的輔助方法來建立未池化的ByteBuf實例。

方法 描述
buffer()
buffer(int initialCapacity)
buffer(int initialCapacity, int maxCapacity)
返回一個未池化的基於堆內存存儲的ByteBuf
directBuffer()
directBuffer(int initialCapacity)
directBuffer(int initialCapacity, int maxCapacity)
返回一個未池化的基於直接內存存儲的ByteBUf
wrappedBuffer() 返回了一個包裝了給定數據的ByteBuf
copiedBuffer() 返回了一個複製了給定數據的ByteBuf

ByteBufUtil類

 ByteBufUtil提供了用於操做ByteBuf的靜態的輔助方法。

方法 描述
hexdump() 以十六進制的表示形式打印ByteBuf的內容
equals(ByteBuf, ByteBuf) 用來判斷兩個ByteBuf實例的相等性

引用計數

 引用計數是一種經過在某個對象所持有的資源再也不被其餘對象引用時釋放該對象所持有的資源來優化內存使用和性能技術。

Netty爲ByteBuf和ByteBufHolder引入了引用計數技術,它們實現了ReferenceCounted接口。

試圖訪問一個已經被釋放的引用計數的對象,將會致使一個IllegalReferenceCountException

相關文章
相關標籤/搜索