1. 程式人生 > >Netty4學習筆記(4)-- ByteBuf和設計模式

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);