Netty4學習筆記(4)-- ByteBuf和設計模式
ByteBuf是Netty框架裡最重要的類之一,簡單的說,ByteBuf就是java.nio.ByteBuffer的Netty版。
ByteBuf邏輯結構
正如類名所反映出來的,ByteBuf邏輯上就是一個byte容器。ByteBuf裡的資料被兩個指標劃分為三個部分,如下圖所示:
- reader index前面的資料是已經讀過的資料,這些資料可以扔掉
- 從reader index開始,到writer index之前的資料是可讀資料
- 從writer index開始,為可寫區域
正是因為這樣的設計,ByteBuf可以同時讀寫資料(只要可讀區域和可寫區域都還有空閒空間),而java.nio.ByteBuffer則必須呼叫flip()
ByteBuf API
ByteBuf提供了大量的方法,比較常用的有下面這些:
- writeXxx(xxx value) 這組方法將不同型別的資料寫到buf裡,同時將writerIndex往前移適當的距離
- readXxx() 這組方法從buf裡讀出某種型別的資料,同時將readerIndex往前移適當的距離
- skipBytes(int length) 將readerIndex往前移指定的距離
- setXxx(int index, xxx value) 這組方法將不同型別的資料寫到buf的指定位置
- getXxx(int index) 這組方法從buf的指定位置讀出一個某種型別的資料
- readerIndex()/writerIndex() 訪問readerIndex和writerIndex
- readerIndex(int)/writerIndex(int) 設定readerIndex和writerIndex
- readableBytes() 返回可讀區域的位元組數
- writableBytes() 返回可寫區域的位元組數
- clear() 清除buf(把readerIndex和writerIndex都設為0)
- discardReadBytes() 扔掉已讀資料
值得一提的是,discardReadBytes()方法需要把可讀資料移動到buf的開頭,因此是個比較慢的操作。而clear()方法只是將兩個指標清0,所以相對而言速度很快。
ByteBufAllocator - 抽象工廠模式
在Netty的世界裡,ByteBuf例項通常應該由ByteBufAllocator來建立。ByteBuf和Allocator的關係如下圖所示:
Allocator的buffer()方法建立ByteBuf例項,ByteBuf的alloc()方法返回建立自己的Allocator。ByteBufAllocator的實現使用了抽象工廠模式,如下圖所示:
CompositeByteBuf - 組合模式
CompositeByteBuf可以讓我們把多個ByteBuf當成一個大Buf來處理,ByteBufAllocator提供了compositeBuffer()工廠方法來建立CompositeByteBuf。CompositeByteBuf的實現使用了組合模式,如下圖所示:
ByteBufInputStream - 介面卡模式
ByteBufInputStream使用介面卡模式,使我們可以把ByteBuf當做Java的InputStream來使用。同理,ByteBufOutputStream允許我們把ByteBuf當做OutputStream來使用。
比如說我們要實現一個自定義的訊息協議,訊息包括header和body兩部分內容,body裡放的是JSON字串。那麼就可以使用ByteBufInputStream來避免把ByteBuf裡的位元組拷貝到位元組陣列的開銷:
private Object decodeMessage(ByteBuf bb) {
// read header
// bb.readXxx()...
// read body
InputStreamReader reader = new InputStreamReader(new ByteBufInputStream(bb));
return new Gson().fromJson(reader, JsonObject.class);
}
ReadOnlyByteBuf - 裝飾器模式
ReadOnlyByteBuf用介面卡模式把一個ByteBuf變為只讀,ReadOnlyByteBuf通過呼叫Unpooled.unmodifiableBuffer(ByteBuf)方法獲得:
類似的ByteBuf介面卡還包括:
ByteBuf - 工廠方法模式
前面也提到過了,我們很少需要直接通過建構函式來建立ByteBuf例項,而是通過Allocator來建立。從裝飾器模式可以看出另外一種獲得ByteBuf的方式是呼叫ByteBuf的工廠方法,比如:
- ByteBuf#duplicate()
- ByteBuf#slice()
ByteBufProcessor - 訪問者模式還是迭代器模式?
最後,ByteBuf提供了4個forEachByte()方法來對ByteBuf裡的資料進行某種處理或查詢,看起來像是訪問者模式和迭代器模式的混合:
- public abstract int forEachByte(ByteBufProcessor processor);
- public abstract int forEachByte(int index, int length, ByteBufProcessor processor);
- public abstract int forEachByteDesc(ByteBufProcessor processor);
- public abstract int forEachByteDesc(int index, int length, ByteBufProcessor processor);