簡介
bytebuf詳解
建立一個buff
随機通路buff
序列讀寫
搜尋
其他衍生buffer方法
和現有jdk類型的轉換
總結
netty中用于進行資訊承載和交流的類叫做bytebuf,從名字可以看出這是byte的緩存區,那麼bytebuf都有哪些特性呢?一起來看看。
netty提供了一個io.netty.buffer的包,該包裡面定義了各種類型的bytebuf和其衍生的類型。
netty buffer的基礎是bytebuf類,這是一個抽象類,其他的buffer類基本上都是由該類衍生而得的,這個類也定義了netty整體buffer的基調。
先來看下bytebuf的定義:
bytebuf實作了兩個接口,分别是referencecounted和comparable。comparable是jdk自帶的接口,表示該類之間是可以進行比較的。而referencecounted表示的是對象的引用統計。當一個referencecounted被執行個體化之後,其引用count=1,每次調用retain() 方法,就會增加count,調用release() 方法又會減少count。當count減為0之後,對象将會被釋放,如果試圖通路被釋放過後的對象,則會報通路異常。
如果一個對象實作了referencecounted,并且這個對象裡面包含的其他對象也實作了referencecounted,那麼當容器對象的count=0的時候,其内部的其他對象也會被調用release()方法進行釋放。
綜上,bytebuf是一個可以比較的,可以計算引用次數的對象。他提供了序列或者随機的byte通路機制。
注意的是,雖然jdk中有自帶的bytebuffer類,但是netty中的 bytebuf 算是對byte buffer的重新實作。他們沒有關聯關系。
bytebuf是一個抽象類,并不能直接用來執行個體化,雖然可以使用bytebuf的子類進行執行個體化操作,但是netty并不推薦。netty推薦使用io.netty.buffer.unpooled來進行buff的建立工作。unpooled是一個工具類,可以為bytebuf配置設定空間、拷貝或者封裝操作。
下面是建立幾個不同bytebuf的例子:
上面我們看到了4種不同的buff建構方式,普通的buff、directbuffer、wrappedbuffer和copiedbuffer。
普通的buff是固定大小的堆buff,而directbuffer是固定大小的direct buff。direct buff使用的是堆外記憶體,省去了資料到核心的拷貝,是以效率比普通的buff要高。
wrappedbuffer是對現有的byte arrays或者byte buffers的封裝,可以看做是一個視圖,當底層的資料發生變化的時候,wrapped buffer中的資料也會發生變化。
copied buffer是對現有的byte arrays、byte buffers 或者 string的深拷貝,是以它和wrappedbuffer是不同的,copied buffer和原資料之間并不共享資料。
熟悉集合的朋友應該都知道,要想随機通路某個集合,一定是通過index來通路的,bytebuf也一樣,可以通過capacity或得其容量,然後通過getbyte方法随機通路其中的byte,如下所示:
讀寫要比通路複雜一點,bytebuf 提供了兩個index用來定位讀和寫的位置,分别是readerindex 和 writerindex ,兩個index分别控制讀和寫的位置。
下圖顯示的一個buffer被分成了三部分,分别是可廢棄的bytes、可讀的bytes和可寫的bytes。
上圖還表明了readerindex、writerindex和capacity的大小關系。
其中readable bytes是真正的内容,可以通過調用read* 或者skip* 的方法來進行通路或者跳過,調用這些方法的時候,readerindex會同步增加,如果超出了readable bytes的範圍,則會抛出indexoutofboundsexception。預設情況下readerindex=0。
下面是一個周遊readable bytes的例子:
首先通過判斷是否是readable來決定是否調用readbyte方法。
writable bytes是一個未确定的區域,等待被填充。可以通過調用write*方法對其操作,同時writerindex 會同步更新,同樣的,如果空間不夠的話,也會抛出indexoutofboundsexception。預設情況下 新配置設定的writerindex =0 ,而wrapped 或者copied buffer的writerindex=buf的capacity。
下面是一個使用writable byte的例子:
discardable bytes是已經被讀取過的bytes,初始情況下它的值=0,每當readerindex右移的時候,discardable bytes的空間就會增加。如果想要完全删除或重置discardable bytes,則可以調用discardreadbytes()方法,該方法會将discardable bytes空間删除,将多餘的空間放到writable bytes中,如下所示:
readerindex (0) <= writerindex (decreased) <= capacity
注意,雖然writable bytes變多了,但是其内容是不可控的,并不能保證裡面的内容是空的或者不變。
調用clear()方法會将readerindex 和 writerindex 清零,注意clear方法隻會設定readerindex 和 writerindex 的值,并不會清空content,看下面的示意圖:
bytebuf提供了單個byte的搜尋功能,如 indexof(int, int, byte) 和 bytesbefore(int, int, byte)兩個方法。
如果是要對bytebuf周遊進行搜尋處理的話,可以使用 foreachbyte(int, int, byteprocessor),這個方法接收一個byteprocessor用于進行複雜的處理。
bytebuf還提供了很多方法用來建立衍生的buffer,如下所示:
要注意的是,這些buf是建立在現有buf基礎上的衍生品,他們的底層内容是一樣的,隻有readerindex, writerindex 和做标記的index不一樣。是以他們和原buf是有共享資料的。如果你希望的是建立一個全新的buffer,那麼可以使用copy()方法或者前面提到的unpooled.copiedbuffer。
在前面小節中,我們講到bytebuf是一個referencecounted,這個特征在衍生buf中就用到了。我們知道調用retain() 方法的時候,引用count會增加,但是對于 duplicate(), slice(), slice(int, int) 和 readslice(int) 這些方法來說,雖然他們也是引用,但是沒有調用retain()方法,這樣原始資料會在任意一個buf調用release()方法之後被回收。
如果不想有上面的副作用,那麼可以将方法替換成retainedduplicate(), retainedslice(), retainedslice(int, int) 和 readretainedslice(int) ,這些方法會調用retain()方法以增加一個引用。
之前提到了bytebuf 是對bytebuffer的重寫,他們是不同的實作。雖然這兩個不同,但是不妨礙将bytebuf轉換bytebuffer。
當然,最簡單的轉換是把bytebuf轉換成byte數組byte[]。要想轉換成byte數組,可以先調用hasarray() 進行判斷,然後再調用array()方法進行轉換。
同樣的bytebuf還可以轉換成為bytebuffer ,可以先調用 niobuffercount()判斷能夠轉換成為 bytebuffers的個數,再調用niobuffer() 進行轉換。
傳回的bytebuffer是對現有buf的共享或者複制,對傳回之後buffer的position和limit修改不會影響到原buf。
最後,使用tostring(charset) 方法可以将bytebuf轉換成為string。
bytebuf是netty的底層基礎,是傳輸資料的承載對象,深入了解bytebuf就可以搞懂netty的設計思想,非常不錯。
本文的例子可以參考:learn-netty4
本文已收錄于 http://www.flydean.com/02-netty-bytebuf/ 最通俗的解讀,最深刻的幹貨,最簡潔的教程,衆多你不知道的小技巧等你來發現! 歡迎關注我的公衆号:「程式那些事」,懂技術,更懂你!