1. 程式人生 > >netty中ByteBuf部分的分析

netty中ByteBuf部分的分析

好久木有看程式碼了,今天把以前讀netty原始碼的時候一直沒有看的Buffer部分粗略的看了下,剛開始主要是覺得buffer這個包裡面類太多了,覺得比較麻煩,而且相對理解整個netty的設計不太影響,所以就拖著沒看,但是記得有一次跟支付寶的技術問了我關於netty中buffer的問題,自己回答的不太好,嗯,該看的還是要看的。。。。

嗯,還是先來一張比較重要的類圖:


嗯,上面最後兩個型別,UnpooledDirectByteBuf以及UnpooledHeapByteBuf就是netty中最終最常用到的兩個buf型別,從名字就能夠看出來他們有什麼區別,其中一種使用到的記憶體是在JVM的堆空間上面分配的,其實底層說白了就是一個byte陣列,而Direct也就是使用的直接記憶體,最終它也是在底層用的其實是java的nio中的bytebuffer。

最上層的ByteBuf是一個抽象類,它定義了很多的抽象方法,這些方法就是netty中bytebuf暴露的所有api,功能還是蠻強大的,netty之所以自己重頭定義了整套的bytebuf就是因為nio類庫中原生的buffer功能太弱的原因。。。。

這裡面比較有意思的方法有如下:

(1)discardReadBytes,它用於對buf裡面的資料進行平移,將已經讀取的資料拋棄,並重用這部分記憶體空間,用如下這張圖來說明:


(2)它定義的read和write方法中,有多種多樣的源以及目的型別,甚至還有channel型別,這就意味著在buf中還是實現從流或者從channel中讀取資料的具體操作。。。。

剩下的就還有很多buf常規的方法了,例如讀取一個位元組,讀取一個整形什麼的。。。。

好了,上面介紹了最終在netty中最常用的兩種buf型別,從名字也可以看出他們不是pool的,在netty中還有一種pool的,實現的比較複雜,但是當然這種效率很高了,不過在實際的程式碼中基本上還是用的unpool的,好啦,而且除了在最開始unsafe物件中從channel中讀取資料的時候常用directbuf外,其餘一般都還是用的undirect的記憶體。。。

那麼我們在程式設計的時候怎麼使用buf呢?一般情況下都是使用一個工具類Unpooled,它定義了很多靜態方法,程式設計時可以用它來生成buf,例如我們經常用如下的形式來生成一個bytebuf物件:

Unpooled.buffer(1000);

嗯,這段程式碼的意思就是生成1000個位元組的buf物件,這裡預設是分配undirect的記憶體,如果要分配direct的,那有專門的方法供呼叫。。。。

但是其實Unpooled最終也是呼叫ByteBufAllocator來建立buf物件的,他們之間的關係如下:


這裡ByteBufAllocator物件預設是UnpooledByteBufAllocator物件,我們可以來看看它的型別定義:

public final class UnpooledByteBufAllocator extends AbstractByteBufAllocator {

    /**
     * Default instance
     */
    public static final UnpooledByteBufAllocator DEFAULT =
            new UnpooledByteBufAllocator(PlatformDependent.directBufferPreferred());

    /**
     * Create a new instance
     *
     * @param preferDirect {@code true} if {@link #buffer(int)} should try to allocate a direct buffer rather than
     *                     a heap buffer
     */
    public UnpooledByteBufAllocator(boolean preferDirect) {
        super(preferDirect);
    }

    @Override
    protected ByteBuf newHeapBuffer(int initialCapacity, int maxCapacity) {
        return new UnpooledHeapByteBuf(this, initialCapacity, maxCapacity);
    }

    @Override
    protected ByteBuf newDirectBuffer(int initialCapacity, int maxCapacity) {
        if (PlatformDependent.hasUnsafe()) {
            return new UnpooledUnsafeDirectByteBuf(this, initialCapacity, maxCapacity);
        } else {
            return new UnpooledDirectByteBuf(this, initialCapacity, maxCapacity);
        }
    }

    @Override
    public boolean isDirectBufferPooled() {
        return false;
    }
}

其實還是蠻簡單的,也主要是實現了兩個生成buf的方法,分別是direct和undirect的。。。。另外它匯出了一個static的本型別物件,七班情況下也就是使用這個物件來分配buf物件。。。

另外在它的父類中定義了很多的分配方法,最基本的有建立heap的buf,也就是undirect的,還有建立direct的buf,另外還有自適應的方法,通過判斷當前的系統型別,自動的來建立這兩種型別的buf中的一種。。。。

另外UnpooledByteBufAllocator其實還是channel的預設的buf分配器。。。來看如下的這段程式碼:

 final ByteBufAllocator allocator = config.getAllocator();  //buffer的allocater
            final int maxMessagesPerRead = config.getMaxMessagesPerRead();

            boolean closed = false;
            Throwable exception = null;
            ByteBuf byteBuf = null;
            int messages = 0;
            try {
                for (;;) {
                    byteBuf = allocHandle.allocate(allocator);
                    int localReadAmount = doReadBytes(byteBuf); //將資料讀進來

這段程式碼就是在channel可以讀取的情況的時候,呼叫內部的unsafe物件來讀取資料的時候要做的操作,這裡首先是獲取ByteBufAllocator物件,待會將會用它來生成用於存放這次讀出來的資料buf,而這最終將會呼叫UnpooledByteBufAllocator自適應的方法來生成buf,最終這裡一般情況下生成的是direct型別的buf。。。。。

好啦,到這裡其實對netty中buf的設計有了比較粗略的大致瞭解,不再是以前那種模模糊糊的了。。。

總的來說實現的還算比較的簡單吧。。。。而且用起來也還算是比較的方便。。。