我們在進(jìn)行數(shù)據(jù)傳送的時(shí)候需要用到緩沖區(qū)擅腰,常用的就是NIO的Buffer,實(shí)際上7種類型(除了Boolean)都有相關(guān)的實(shí)現(xiàn)枷莉,但是NIO編程常用的還是ByteBuffer熬词。但是ByteBuffer有position轮听,limit卷谈,capacity位置杯拐,每次讀寫還需要flip和clear處理相對比較麻煩,所以Netty自己使用ByteBuf來替換NIO的緩沖。
1.ByteBuf的原理
ByteBuf通過兩個(gè)指針來協(xié)助緩沖區(qū)的讀寫操作端逼,讀操作用readerIndex朗兵,寫操作用writeIndex;
readerIndex和writerIndex的取值一開始都是0顶滩,隨著數(shù)據(jù)的寫入writerIndex會增加,讀取數(shù)據(jù)會使readerIndex增加矛市,但是它不會超過writerIndex.在讀取之后,0~readerIndex的就被視為discard的诲祸,調(diào)用discardReadBytes方法,可以釋放這部分空間而昨,它的作用類似ByteBuffer的compact方法救氯。 ReaderIndex和writerIndex 之間的數(shù)據(jù)是可讀取的,等價(jià)于ByteBuffer position和limit之間的數(shù)據(jù)歌憨。WriterIndex 和capacity之間的空間是可寫的着憨,等價(jià)于ByteBuffer limit 和capacity之間的可用空間。
由于寫操作不修改readerIndex指針务嫡,讀操作不修改writerIndex指針甲抖,因此讀寫之間不再需要調(diào)整位置指針,這極大地簡化了緩沖區(qū)的讀寫操作心铃,避免了由于遺漏或者不熟悉flip()操作導(dǎo)致的功能異常准谚。
具體操作流程如下圖:
2.ByteBuf的功能
2.1 順序讀read操作
byteBuf的read操作ByteBuffer的get操作,有許多可以查看相關(guān)api去扣。
比如readBytes()
2.2 順序?qū)憌rite操作
byteBuf的write操作ByteBuffer的put操作柱衔,有許多可以查看相關(guān)api。
比如writeBytes()
2.3 readerIndex和writerIndex
Netty提供了兩個(gè)指針變量用于支持順序讀取和寫入操作:readerIndex用于標(biāo)識讀取索引愉棱,writerIndex 用于標(biāo)識寫入索引唆铐。兩個(gè)位置指針將ByteBuf緩沖區(qū)分割成三個(gè)區(qū)域.
調(diào)用ByteBuf 的read操作時(shí),從readerIndex處開始讀取奔滑。readerIndex 到writerIndex之間的空間為可讀的字節(jié)緩沖區(qū);從writerIndex到cappacitcity 之間為可寫的字節(jié)緩沖區(qū); 0到readerIndex之間是已經(jīng)讀取過的緩沖區(qū)艾岂,可以調(diào)用discardReadBytes操作來重用這部分空間,以節(jié)約內(nèi)存朋其,防止ByteBuf的動(dòng)態(tài)擴(kuò)張王浴。這在私有協(xié)議棧消息解碼的時(shí)候非常有用,因?yàn)門CP底層可能粘包令宿,幾百個(gè)整包消息被TCP粘包后作為一個(gè)整包發(fā)送叼耙,這樣,通過discardReadBytes操作可以重用之前已經(jīng)解碼過的緩沖區(qū)粒没,這樣就可以防止接收緩沖區(qū)因?yàn)槿萘坎蛔銓?dǎo)致的擴(kuò)張筛婉。
2.4 Discardable bytes
清理掉已經(jīng)讀取到的緩沖區(qū)。
2.5 ReadableBytes 和WritableBytes
可以查看可讀字節(jié)以及可寫的字節(jié)數(shù)
2.6 Clear,SkipBytes操作
clear 會將readerIndex和writerIndex全部設(shè)置為0,內(nèi)容不會清除
SkipBytes跳過不需要讀取的字節(jié)或者字節(jié)數(shù)組爽撒。
2.7 Mark和Reset操作
如果某個(gè)時(shí)候我們需要對于某一個(gè)操作做回滾入蛆,那么mark和reset就是這樣的實(shí)現(xiàn)的,他們只是改變索引位置硕勿,并不會 改變緩沖區(qū)內(nèi)容哨毁。通過mark操作將當(dāng)前指針備份到mark中,當(dāng)調(diào)用reset操作恢復(fù)備份的mark值源武。
比如:
markReaderIndex
resetReaderIndex
markWriterIndex
resetWriterIndex
2.8 查找操作
我們可以通過某一個(gè)字符定位他的位置做到定位操作扼褪。
比如indexOf,bytesBefore,forEachByte等。
2.9 Derived buffers 新建視圖
類似數(shù)據(jù)庫視圖粱栖,可以創(chuàng)建byteBuf的視圖或者復(fù)制byteBuf
1)duplicate:返回當(dāng)前ByteBuf的復(fù)制對象话浇,復(fù)制的只是指針,內(nèi)容還是原來的內(nèi)容闹究,如果原來的內(nèi)容變了幔崖,之后的byteBuf獲得的內(nèi)容也是變化后的。
2)copy:完全復(fù)制新的ByteBuf渣淤,內(nèi)容和索引都是完全互不影響
3)slice:返回當(dāng)前可讀緩沖區(qū)即readerIndex到writerIndex赏寇,返回后的ByteBuf和原來的共享內(nèi)容,但是指針各種維護(hù)
2.10 轉(zhuǎn)換為標(biāo)準(zhǔn)的ByteBuffer
將byteBuf轉(zhuǎn)換為對應(yīng)的ByteBuffer
2.11 隨機(jī)讀寫(set/get)
除了順序的read和write外价认,也支持隨機(jī)讀寫嗅定,不過不同的地方就是順序讀寫可以做到自動(dòng)擴(kuò)容,隨機(jī)讀寫會對索引和長度進(jìn)行校驗(yàn)刻伊,不會動(dòng)態(tài)擴(kuò)展緩沖區(qū)露戒,所以需要注意字節(jié)長度,否則會拋出越界的錯(cuò)誤捶箱。
3.ByteBuf的輔助類
3.1 ByteBufHolder
ByteBufHolder是ByteBuf的容器智什,在Netty中,它非常有用丁屎,例如HTTP協(xié)議的請求消息和應(yīng)答消息都可以攜帶消息體荠锭,這個(gè)消息體在NIOByteBuffer中就是個(gè)ByteBuffer對象,在Netty中就是ByteBuf對象晨川。由于不同的協(xié)議消息體可以包含不同的協(xié)議字段和功能证九,因此,需要對ByteBuf進(jìn)行包裝和抽象共虑,不同的子類可以有不同的實(shí)現(xiàn)愧怜。
為了滿足這些定制化的需求,Netty 抽象出了ByteBufHolder 對象妈拌,它包含了一個(gè)ByteBuf,另外還提供了一些其他實(shí)用的方法拥坛,使用者繼承ByteBufHolder接口后可以按需封裝自己的實(shí)現(xiàn)蓬蝶。
3.2 ByteBufferAllocator
ByteBufAllocator是字節(jié)緩沖區(qū)分配器,按照Netty的緩沖區(qū)實(shí)現(xiàn)不同猜惋,共有兩種不同的分配器:基于內(nèi)存池的字節(jié)緩沖區(qū)分配器和普通的字節(jié)緩沖區(qū)分配器丸氛。
UnpooledByteBufAllocator
PooledByteBufAllocator
他們分別可以創(chuàng)建堆內(nèi)緩沖和非堆內(nèi)緩沖
3.3 CompositeByteBuf
CompositeByteBuf允許將多個(gè)ByteBuf的實(shí)例組裝到一起,形成-一個(gè)統(tǒng)- -的視圖著摔,有點(diǎn)類似于數(shù)據(jù)庫將多個(gè)表的字段組裝到一起統(tǒng)一用視圖展示缓窜。
3.4 ByteBufUtil
ByteBufUtil是一個(gè)非常有用的工具類,它提供了一系列靜態(tài)方法用于操作ByteBuf對象.
比較有用的比如encodeString,decodeString,hexDump等方法谍咆。
4. 內(nèi)存分配和回收
從內(nèi)存分配的角度看禾锤,ByteBuf 可以分為兩類:
(1)堆內(nèi)存(HeapByteBuf) 字節(jié)緩沖區(qū):特點(diǎn)是內(nèi)存的分配和回收速度快,可以被 JVM自動(dòng)回收;缺點(diǎn)就是如果進(jìn)行Socket 的I/O 讀寫摹察,需要額外做一次內(nèi)存復(fù)制时肿,將堆 內(nèi)存對應(yīng)的緩沖區(qū)復(fù)制到內(nèi)核Channel中,性能會有一定程度的下降港粱。
(2)直接內(nèi)存( DirectByteBuf) 字節(jié)緩沖區(qū):非堆內(nèi)存,它在堆外進(jìn)行內(nèi)存分配旦签,相 比于堆內(nèi)存查坪,它的分配和回收速度會慢--些,但是將它寫入或者從SocketChannel中讀取 時(shí)宁炫,由于少了一次內(nèi)存復(fù)制偿曙,速度比堆內(nèi)存快。
正是因?yàn)楦饔欣赘岢玻訬etty提供了多種ByteBuf供開發(fā)者使用望忆,經(jīng)驗(yàn)表明,ByteBuf 的最佳實(shí)踐是在I/O通信線程的讀寫緩沖區(qū)使用DirectByteBuf,后端業(yè)務(wù)消息的編解碼模 塊使用HeapByteBuf,這樣組合可以達(dá)到性能最優(yōu)竿秆。
從內(nèi)存回收角度看启摄,ByteBuf也分為兩類:基于對象池的ByteBuf和普通ByteBuf。兩 者的主要區(qū)別就是基于對象池的ByteBuf可以重用ByteBuf對象幽钢,它自己維護(hù)了一個(gè)內(nèi)存 池歉备,可以循環(huán)利用創(chuàng)建的ByteBuf,提升內(nèi)存的使用效率,降低由于高負(fù)載導(dǎo)致的頻繁GC匪燕。 測試表明使用內(nèi)存池后的Netty在高負(fù)載蕾羊、大并發(fā)的沖擊下內(nèi)存和GC更加平穩(wěn)。 盡管推薦使用基于內(nèi)存池的ByteBuf,但是內(nèi)存池的管理和維護(hù)更加復(fù)雜帽驯,使用起來 也需要更加謹(jǐn)慎龟再,因此,Netty提供了靈活的策略供使用者來做選擇尼变。
參考:《Netty權(quán)威指南》