【Netty專題】源碼剖析netty核心基礎(chǔ)ByteBuf

??????? 在面試的時候,當面試問到netty的時候問到:你知道jdk nio中的ByteBuffer與netty?中的ByteBuf有什么區(qū)別嗎?來看看面試者的基礎(chǔ)掌握的如何!你能準確回到出來個所以然嗎?

????? 說到j(luò)dk我先說說我身邊使用jdk nio的情況;

????? 我現(xiàn)在公司就有個游戲項目是jdk nio2一行一行實現(xiàn)的通訊架構(gòu),一直在線上運營,目前該架構(gòu)單服承載最高的時候達到3000多人,沒發(fā)現(xiàn)有什么性能瓶頸,當然人數(shù)可能還會繼續(xù)增加,只要提高服務(wù)器配置,或者選擇增加新的服務(wù)器去負載均衡;

?????? 我們都知道jdk nio臭名昭著的epoll bug,它會導(dǎo)致Selector空輪詢,最終導(dǎo)致CPU 100%。官方聲稱在JDK1.6版本的update18修復(fù)了該問題;當然我沒有遇到這個bug,希望后面也不要遇到(祈禱),而netty的設(shè)計就很自然的避免了這個問題;不用擔(dān)心這個bug出現(xiàn);

?????? 這篇文章就是由淺入深解讀ByteBuf家族,從而徹底掌握ByteBuf各知識點。

一、先來比較下jdk nio中的ByteBuffer與netty?中的ByteBuf

JDK NIO 劣勢:

1、ByteBuffer給開發(fā)者的第一感覺就是他的API太少了,程序員的很多需求都不能滿足,只能自己去實現(xiàn)或者做一下封裝;

2、ByteBuffer 長度固定,不能動態(tài)收縮拓展,如果存很長的數(shù)據(jù)時候,就容易越界報錯,例如:

3、ByteBuffer 讀寫操作的時候只有一個索引,操作起來flip(),rewind(),讓人蛋疼,小白操作很容易混亂,這一點Mina IoBuffer和ByteBuffer同病相憐;

相反netty 的ByteBuf解決上面的所有的痛點;豐富的API,支持動態(tài)收縮拓展,并且讀寫索引分開,不用操作字節(jié)的時候,那么蛋疼;

二、ByteBuf實現(xiàn)原理以及如何使用

Netty ByteBuf提供了2個指針變量分別用于順序讀取,和順序?qū)懭?,readerIndex是讀索引,writerIndex是寫索引,二者劃分ByteBuf緩沖區(qū)關(guān)系:


capacity:

?緩沖區(qū)的容量。

readerIndex:

讀取字節(jié),改變readerIndex位置,或者下面的方法去初始化readerIndex

設(shè)置當前讀的位置??梢允褂胷eaderIndex()和readerIndex(int)方法獲取、設(shè)置readerIndex值。每次調(diào)用readXXX方法都會導(dǎo)致readerIndex向writerIndex移動,直到等于writerIndex為止。

writerIndex:

設(shè)置寫的當前位置??梢允褂脀riterIndex()和writerIndex(int)方法獲取、設(shè)置writeIndex的值。每次調(diào)用writeXXX方法都會導(dǎo)致writeIndex向capacity移動,直到等于capacity為止。

discardable bytes:

表示讀取之后丟棄,節(jié)約空間資源。0--readerIndex之間的數(shù)據(jù),?長度是readerIndex - 0,調(diào)用discardReadBytes會丟棄這部分數(shù)據(jù),把readerIndex--writerIndex之間的數(shù)據(jù)移動到ByteBuf的開始位置(0);

使用案例展示:

三、ByteBuf緩沖分類

1、Heap buffer(堆緩沖區(qū)):

就是將數(shù)據(jù)存在JVM堆空間中,在沒有被池化的情況可以快速分配和釋放。

優(yōu)點:由于數(shù)據(jù)是存儲在JVM堆中,因此可以快速的創(chuàng)建與快速的釋放,并且它提供了直接訪問內(nèi)部字節(jié)數(shù)組的方法。

缺點:每次讀寫數(shù)據(jù)時,都需要先將數(shù)據(jù)復(fù)制到直接緩沖區(qū)中再進行網(wǎng)路傳輸。

2、Direct buffer(直接緩沖區(qū)):

直接緩沖區(qū),在堆外直接分配內(nèi)存空間,直接緩沖區(qū)并不會占用堆的容量空間,因為它是由操作系統(tǒng)在本地內(nèi)存進行的數(shù)據(jù)分配。

優(yōu)點:在使用Socket進行數(shù)據(jù)傳遞時,性能非常好,因為數(shù)據(jù)直接位于操作系統(tǒng)的本地內(nèi)存中,所以不需要從JVM將數(shù)據(jù)復(fù)制到直接緩沖區(qū)中 。

缺點:因為Direct Buffer是直接在操作系統(tǒng)內(nèi)存中的,所以內(nèi)存空間的分配與釋放要比堆空間更加復(fù)雜,而且速度要慢一些。

注意:

如果你的數(shù)據(jù)包含在一個在堆上的分配的緩沖區(qū)中,那么事實上,在通過套接字發(fā)送他之前,jvm將會在內(nèi)部把你的緩沖區(qū)復(fù)制到一個直接緩沖區(qū)中;這樣分配釋放就比較浪費資源;

建議:

直接緩沖區(qū)并不支持通過字節(jié)數(shù)組的方式來訪問數(shù)據(jù)。對于后端業(yè)務(wù)的消息編解碼來說,推薦使用HeapByteBuf;對于I/O通信線程在讀寫緩沖區(qū)時,推薦使用DirectByteBuf;

3、Composite Buffer?復(fù)合緩沖區(qū):

可以擁有以上兩種的緩沖區(qū),通過一種聚合視圖來操作底層持有的多種類型Buffer。這種緩沖,jdk nio是沒有這種特性的。

四、源碼解讀ByteBuf家族



先看下ByteBuf的接口和他的三個不同方向的實現(xiàn)抽象類,下面還有具體實現(xiàn)類后面再具體列出來講解,先介紹下這幾個ByteBuf的頂級接口和抽象父類:

(Deprecated?的SwappedByteBuf官方已經(jīng)不贊成去使用了,是不安全緩沖接口)

1、ReferenceCounted引用計數(shù)器接口。

Netty 4開始,對象的生命周期由它們的引用計數(shù)(reference counts)管理,而不是由垃圾收集器(garbage collector)管理了。ByteBuf是最值得注意的,它使用了引用計數(shù)來改進分配內(nèi)存和釋放內(nèi)存的性能。ByteBuf利用引用計數(shù)來改進分配和回收性能;

? ? ??只要引用計數(shù)大于?0,就能保證對象不會被釋放。當活動引用的數(shù)量減少到0?時,該實例就會被釋放;是由最后訪問(引用計數(shù))對象的那一方來負責(zé)將它釋放。

(1)、如果一個對象實現(xiàn)了ReferenceCounted接口,被初始化的時候,計數(shù)為1。

(2)、retain()方法能夠增加計數(shù),release()?方法能夠減少計數(shù),如果計數(shù)被減少到0則對象會被顯示回收,再次訪問被回收的這些對象將會拋出異常。

(3)、如果一個對象實現(xiàn)了ReferenceCounted,并且包含有其他對象也實現(xiàn)來ReferenceCounted,當這個對象計數(shù)為0被回收的時候,所包含的對象同樣會通過release()釋放掉。

2、Comparable:排序接口。

ByteBuf:定義了一下是否可讀寫的屬性以及定義了一些讀寫操作的抽象接口,供子類繼承實現(xiàn)。

3、WrappedByteBuf:用于裝飾ByteBuf對象,主要有AdvancedLeakAwareByteBuf、SimpleLeakAwareByteBuf和UnreleasableByteBuf三個子類。

(1)、WrappedByteBuf使用裝飾者模式裝飾ByteBuf對象

(2)、AdvancedLeakAwareByteBuf用于對所有操作記錄堆棧信息,方便監(jiān)控內(nèi)存泄漏;

(3)、SimpleLeakAwareByteBuf只記錄order(ByteOrder endianness)的堆棧信息;

(4)、UnreleasableByteBuf用于阻止修改對象引用計數(shù)器refCnt的值。

4、AbstractByteBuf:抽象繼承ByteBuf,?是ByteBuf緩沖的默認實現(xiàn)接口.

子類很多,我們上面說的三種緩沖策略的類實現(xiàn)都是最終繼承實現(xiàn)AbstracByteBuf,而AbstractByteBuf本身并沒有具體去對不同ByteBuf緩沖區(qū)去做具體實現(xiàn),而是由子類去實現(xiàn),原因很簡單父類不知道子類去實現(xiàn)堆內(nèi)存還是直接內(nèi)存,還是復(fù)合緩沖區(qū);只是提供一個抽象接口而已;

再看看AbstractByteBuf源碼中屬性:

定義了讀,寫索引和讀寫索引的標記,以及最大容量,leakDetectro:是監(jiān)測內(nèi)存是否泄漏,是static類型,說明是共享公共的對象;

AbstractByteBuf直接子類AbstractDerivedByteBuf:提供派生ByteBuf的默認實現(xiàn),主要有DuplicatedByteBuf、ReadOnlyByteBuf和SlicedByteBuf。

(1)、DuplicatedByteBuf:使用裝飾者模式創(chuàng)建ByteBuf的復(fù)制對象,使得復(fù)制后的對象與原對象共享緩沖區(qū)的內(nèi)容,但是獨立維護自己的readerIndex和writerIndex。

(2)、ReadOnlyByteBuf:使用裝飾者模式創(chuàng)建ByteBuf的只讀對象,該只讀對象與原對象共享緩沖區(qū)的內(nèi)容,但是獨立維護自己的readerIndex和writerIndex,之后所有的寫操作都被限制;

(3)、SlicedByteBuf:使用裝飾者模式創(chuàng)建ByteBuf的一個子區(qū)域ByteBuf對象,返回的ByteBuf對象與當前ByteBuf對象共享緩沖區(qū)的內(nèi)容,但是維護自己獨立的readerIndex和writerIndex,允許寫操作。

(4)、其實還可以按照另外一個維度去理解,一個是池化的ByteBuf一個是非池化的ByteBuf(用完就銷毀);即PooledByteBuf_ 和UnpooledByteBuf_;

以上三種緩沖官方已經(jīng)Deprecated不推介使用;

這也不推介使用那也不推介使用,那我們用哪些類去操作緩沖呢?

我們用AbstractByteBuf直接子類AbstractReferenceCountedByteBuf,該抽象類的子類實現(xiàn)的緩沖類;

上面我們看到很多實現(xiàn)類,而且沒有一個被Deprecated的;這里只重點介紹幾個常用的;

(1)、UnpooledDirectByteBuf:

?在堆外進行內(nèi)存分配的非內(nèi)存池ByteBuf,內(nèi)部持有ByteBuffer對象,相關(guān)操作委托給ByteBuffer實現(xiàn)。

(2)、UnpooledHeapByteBuf:

? 基于堆內(nèi)存分配非內(nèi)存池ByteBuf,即內(nèi)部持有byte數(shù)組。

(3)、UnpooledUnsafeDirectByteBuf:

? 和另外一個類UnpooledDirectByteBuf差不多相同,區(qū)別在于UnpooledUnsafeDirectByteBuf內(nèi)部使用基于PlatformDependent相關(guān)操作實現(xiàn)ByteBuf,依賴平臺。

(4)、ReadOnlyByteBufferBuf:

? 只讀ByteBuf,內(nèi)部持有ByteBuffer對象,相關(guān)操作委托給ByteBuffer實現(xiàn),該ByteBuf限內(nèi)部使用;

(5)、FixedCompositeByteBuf:

?用于將多個ByteBuf組合在一起,形成一個虛擬的只讀ByteBuf對象,不允許寫入和動態(tài)擴展。內(nèi)部使用Object[]將多個ByteBuf組合在一起,一旦FixedCompositeByteBuf對象構(gòu)建完成,則不會被更改。

(6)、CompositeByteBuf:

?用于將多個ByteBuf組合在一起,形成一個虛擬的ByteBuf對象,支持讀寫和動態(tài)擴展。內(nèi)部使用List組合多個ByteBuf。一般使用使用ByteBufAllocator的compositeBuffer()方法,Unpooled的工廠方法compositeBuffer()或wrappedBuffer(ByteBuf... buffers)創(chuàng)建CompositeByteBuf對象。

(7)、PooledByteBuf:

? 基于內(nèi)存池的ByteBuf,主要為了重用ByteBuf對象,提升內(nèi)存的使用效率;適用于高負載,高并發(fā)的應(yīng)用中。主要有PooledDirectByteBuf,PooledHeapByteBuf,PooledUnsafeDirectByteBuf三個子類,PooledDirectByteBuf是在堆外進行內(nèi)存分配的內(nèi)存池ByteBuf,PooledHeapByteBuf是基于堆內(nèi)存分配內(nèi)存池ByteBuf,PooledUnsafeDirectByteBuf也是在堆外進行內(nèi)存分配的內(nèi)存池ByteBuf,區(qū)別在于PooledUnsafeDirectByteBuf內(nèi)部使用基于PlatformDependent相關(guān)操作實現(xiàn)ByteBuf,具有平臺相關(guān)性。

????? 就到這里了,感謝讀者認真讀完;差不多就netty的ByteBuf家族有了初步的了解了,后面博客我會對三種緩沖(直接緩沖,復(fù)合緩沖,堆緩沖)進行具體源碼剖析,和大家一起學(xué)習(xí)一起探討;如有些的不對的地方,請積極指出,以便及時糾正。

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容