Netty源碼愫讀(一)ByteBuf相關(guān)源碼學(xué)習(xí)

在Java NIO相關(guān)的組件中昏名,ByteBuffer是除了Selector渠抹、Channel之外的另一個(gè)很重要的組件樱报,它是直接和Channel打交道的緩沖區(qū)葬项,通常場(chǎng)景或是從ByteBuffer寫(xiě)入Channel,或是從Channel讀入Buffer迹蛤;
在Netty中民珍,被精心設(shè)計(jì)的ByteBuf則是Netty貫穿整個(gè)開(kāi)發(fā)過(guò)程中的核心緩沖區(qū)襟士,其簡(jiǎn)化了ByteBuffer的操作,同時(shí)提供與ByteBuffer互操作api嚷量,方便了應(yīng)用的開(kāi)發(fā)敌蜂。

關(guān)于Java NIO的ByteBuffer請(qǐng)參考:http://www.reibang.com/p/49d20a7547f6

1、ByteBuf功能說(shuō)明

1.1津肛、ByteBuffer的缺點(diǎn)

從功能角度而言,ByteBuffer完全可以滿足NIO編程的需要汗贫,但是由于NIO編程的復(fù)雜性身坐,ByteBuffer也有其局限性,它的主要缺點(diǎn)如下:

  • ByteBuffer長(zhǎng)度固定落包,一旦分配完成部蛇,它的容量不能動(dòng)態(tài)擴(kuò)展和收縮,當(dāng)需要編碼的POJO對(duì)象大于ByteBuffer的容量時(shí)咐蝇,會(huì)發(fā)生索引越界異常涯鲁;
  • ByteBuffer只有一個(gè)標(biāo)識(shí)位置的指針position,讀寫(xiě)的時(shí)候需要手工調(diào)用flip()和rewind()等有序,使用者必須小心謹(jǐn)慎地處理這些API抹腿,否則很容易導(dǎo)致程序處理失敗旭寿;
  • ByteBuffer的API功能有限警绩,一些高級(jí)和實(shí)用的特性它不支持,需要使用者自己編程實(shí)現(xiàn)盅称。

為了彌補(bǔ)這些不足肩祥,Netty提供了自己的ByteBuffer實(shí)現(xiàn)——ByteBuf。

1.2缩膝、ByteBuf原理

ByteBuf通過(guò)以下位置指針以簡(jiǎn)化緩沖區(qū)操作:

  • readerIndex:讀操作指針混狠,標(biāo)識(shí)讀起始位置;
  • writerIndex:寫(xiě)操作指針疾层,標(biāo)識(shí)寫(xiě)起始位置将饺;
  • maxCapacity:緩沖區(qū)容量指針,標(biāo)識(shí)緩沖區(qū)的容量大性坡俯逾;
  • markedReaderIndex:讀標(biāo)記指針,記錄readerIndex值舅逸;
  • markedWriterIndex:讀標(biāo)記指針桌肴,記錄writterIndex值;

readerIndex和writerIndex的取值一開(kāi)始都是0琉历,隨著數(shù)據(jù)的寫(xiě)入writerIndex會(huì)增加坠七,讀取數(shù)據(jù)會(huì)使readerIndex增加水醋,但是它不會(huì)超過(guò)writerIndex。

在讀取之后彪置,0~readerIndex的就被視為discard的拄踪,調(diào)用discardReadBytes()方法,可以釋放這部分空間拳魁,它的作用類似ByteBufferd的compact()方法惶桐。readerIndex和writerIndex之間的數(shù)據(jù)是可讀取的,等價(jià)于ByteBuffer的position和limit之間的數(shù)據(jù)潘懊。writerIndex和capacity之間的空間是可寫(xiě)的姚糊,等價(jià)于ByteBuffer的limit和capacity之間的可用空間。

由于寫(xiě)操作不修改readerIndex指針授舟,讀操作不修改writerIndex指針救恨,因此讀寫(xiě)之間不再需要調(diào)整位置指針,這極大地簡(jiǎn)化了緩沖區(qū)的讀寫(xiě)操作释树,避免了由于遺漏或者不熟悉flip()操作導(dǎo)致的功能異常肠槽。

初始分配的ByteBuf如圖:

初始分配的ByteBuf.png

寫(xiě)入N字節(jié)后的ByteBuf如圖:

寫(xiě)入N字節(jié)后的ByteBuf.png

讀取M(M<N)字節(jié)后的ByteBuf如圖:

讀取M字節(jié)后的ByteBuf.png

調(diào)用discarReadBytes()之后的ByteBuf如圖:

調(diào)用discarReadBytes()之后的ByteBuf.png

調(diào)用clear()操作之后的ByteBuf如圖:

調(diào)用clear()操作之后的ByteBuf.png

1.3、動(dòng)態(tài)擴(kuò)容

當(dāng)我們對(duì)ByteBuffer進(jìn)行put操作的時(shí)候奢啥,如果緩沖區(qū)剩余可寫(xiě)空間不夠秸仙,就會(huì)發(fā)生 BufferOverflowException異常。為了避免發(fā)生這個(gè)問(wèn)題扫尺,通常在進(jìn)行put操作的時(shí)候會(huì)對(duì)剩余可用空間進(jìn)行校驗(yàn)筋栋,如果剩余空間不足,需要重新創(chuàng)建一個(gè)新的ByteBuffer正驻,并將之前的ByteBuffer復(fù)制到新創(chuàng)建的ByteBuffer中弊攘,最后釋放老的ByteBuffer。

if(this.buffer.remaining() < needSize) {
  int toBeExtSize = needSize < 128 ? needSize : 128;
  ByteBuffer tmpBuffer = ByteBuffer.allocate(this.buffer.capacity() + toBeExtSize);
  this.buffer.flip();
  tmpBuffer.put(this.buffer);
  this.buffer = tmpBuffer;
}

從示例代碼可以看出姑曙,為了防止ByteBuffer溢出襟交,每進(jìn)行一次put操作,都需要對(duì)可用空間進(jìn)行校驗(yàn)伤靠,這導(dǎo)致了代碼冗余捣域,稍有不慎,就可能引入其他問(wèn)題宴合。為了解決這個(gè)問(wèn)題焕梅,ByteBuf對(duì)write操作進(jìn)行了封裝,由ByteBuf的write操作負(fù)責(zé)進(jìn)行剩余可用空間的校驗(yàn)卦洽,如果可用緩沖區(qū)不足贞言,ByteBuf會(huì)自動(dòng)進(jìn)行動(dòng)態(tài)擴(kuò)展,對(duì)于使用者而言阀蒂,不需要關(guān)心底層的校驗(yàn)和擴(kuò)展細(xì)節(jié)该窗,只要不超過(guò)設(shè)置的最大緩沖區(qū)容量即可弟蚀。當(dāng)可用空間不足時(shí),ByteBuf會(huì)幫助我們實(shí)現(xiàn)自動(dòng)擴(kuò)展酗失。

    @Override
    public ByteBuf ensureWritable(int minWritableBytes) {
        if (minWritableBytes < 0) {
            throw new IllegalArgumentException(String.format(
                    "minWritableBytes: %d (expected: >= 0)", minWritableBytes));
        }
        ensureWritable0(minWritableBytes);
        return this;
    }
final void ensureWritable0(int minWritableBytes) {
        ensureAccessible();
        if (minWritableBytes <= writableBytes()) {
            return;
        }

        if (minWritableBytes > maxCapacity - writerIndex) {
            throw new IndexOutOfBoundsException(String.format(
                    "writerIndex(%d) + minWritableBytes(%d) exceeds maxCapacity(%d): %s",
                    writerIndex, minWritableBytes, maxCapacity, this));
        }

        // Normalize the current capacity to the power of 2.
        int newCapacity = alloc().calculateNewCapacity(writerIndex + minWritableBytes, maxCapacity);

        // Adjust to the new capacity.
        capacity(newCapacity);
    }

通過(guò)源碼分析义钉,我們發(fā)現(xiàn)當(dāng)進(jìn)行write操作時(shí)會(huì)對(duì)需要write的字節(jié)進(jìn)行校驗(yàn),如果可寫(xiě)的字節(jié)數(shù)小于需要寫(xiě)入的字節(jié)數(shù)规肴,并且需要寫(xiě)入的字節(jié)數(shù)小于可寫(xiě)的最大字節(jié)數(shù)時(shí)捶闸,對(duì)緩沖區(qū)進(jìn)行動(dòng)態(tài)擴(kuò)展。無(wú)論緩沖區(qū)是否進(jìn)行了動(dòng)態(tài)擴(kuò)展拖刃,從功能角度看使用者并不感知鉴嗤,這樣就簡(jiǎn)化了上層的應(yīng)用。

1.4序调、ByteBuf操作介紹

1.4.1、順序讀操作(read):

方法名稱 返回值 功能說(shuō)明 拋出異常
readBoolean() boolean 從readerIndex開(kāi)始讀取1字節(jié)的數(shù)據(jù) throwsIndexOutOfBoundsExceptionreadableBytes<1
readByte() byte 從readerIndex開(kāi)始讀取1字節(jié)的數(shù)據(jù) throws IndexOutOfBoundsExceptionreadableBytes<1
readUnsignedByte() short 從readerIndex開(kāi)始讀取1字節(jié)的數(shù)據(jù)(無(wú)符號(hào)字節(jié)值) throws IndexOutOfBoundsException:readableBytes<1
readShort() short 從readerIndex開(kāi)始讀取16位的短整形值 throws IndexOutOfBoundsException:readableBytes<2
readUnsignedShort() int 從readerIndex開(kāi)始讀取16位的無(wú)符號(hào)短整形值 throws IndexOutOfBoundsException:readableBytes<2
readMedium() int 從readerIndex開(kāi)始讀取24位的整形值兔簇,(該類型并非java基本類型发绢,通常不用) throws IndexOutOfBoundsException:readableBytes<3
readUnsignedMedium() int 從readerIndex開(kāi)始讀取24位的無(wú)符號(hào)整形值,(該類型并非java基本類型垄琐,通常不用) throws IndexOutOfBoundsException:readableBytes<3
readInt() int 從readerIndex開(kāi)始讀取32位的整形值 throws IndexOutOfBoundsException:readableBytes<4
readUnsignedInt() long 從readerIndex開(kāi)始讀取32位的無(wú)符號(hào)整形值 throws IndexOutOfBoundsException:readableBytes<4
readLong() long 從readerIndex開(kāi)始讀取64位的整形值 throws IndexOutOfBoundsException:readableBytes<8
readChar() char 從readerIndex開(kāi)始讀取2字節(jié)的字符值 throws IndexOutOfBoundsException:readableBytes<2
readFloat() float 從readerIndex開(kāi)始讀取32位的浮點(diǎn)值 throws IndexOutOfBoundsException:readableBytes<4
readDouble() double 從readerIndex開(kāi)始讀取64位的浮點(diǎn)值 throws IndexOutOfBoundsException:readableBytes<8
readBytes(int length) ByteBuf 將當(dāng)前ByteBuf中的數(shù)據(jù)讀取到新創(chuàng)建的ByteBuf中边酒,從readerIndex開(kāi)始讀取length字節(jié)的數(shù)據(jù)。返回的ByteBuf readerIndex 為0狸窘,writeIndex為length墩朦。 throws IndexOutOfBoundsException:readableBytes<length
readSlice(int length) ByteBuf 返回當(dāng)前ByteBuf新創(chuàng)建的子區(qū)域,子區(qū)域和原ByteBuf共享緩沖區(qū)的內(nèi)容翻擒,但獨(dú)立維護(hù)自己的readerIndex和writeIndex氓涣,新創(chuàng)建的子區(qū)域readerIndex 為0,writeIndex為length陋气。 throws IndexOutOfBoundsException:readableBytes<length
readBytes(ByteBuf dst) ByteBuf 將當(dāng)前ByteBuf中的數(shù)據(jù)讀取到目標(biāo)ByteBuf (dst)中劳吠,從當(dāng)前ByteBuf readerIndex開(kāi)始讀取,直到目標(biāo)ByteBuf無(wú)可寫(xiě)空間巩趁,從目標(biāo)ByteBuf writeIndex開(kāi)始寫(xiě)入數(shù)據(jù)痒玩。讀取完成后,當(dāng)前ByteBuf的readerIndex+=讀取的字節(jié)數(shù)议慰。目標(biāo)ByteBuf的writeIndex+=讀取的字節(jié)數(shù)蠢古。 throws IndexOutOfBoundsException:this.readableBytes<dst.writableBytes
readBytes(ByteBuf dst, int length) ByteBuf 將當(dāng)前ByteBuf中的數(shù)據(jù)讀取到目標(biāo)ByteBuf (dst)中,從當(dāng)前ByteBuf readerIndex開(kāi)始讀取别凹,長(zhǎng)度為length草讶,從目標(biāo)ByteBuf writeIndex開(kāi)始寫(xiě)入數(shù)據(jù)。讀取完成后番川,當(dāng)前ByteBuf的readerIndex+=length到涂,目標(biāo)ByteBuf的writeIndex+=length throws IndexOutOfBoundsException:this.readableBytes<length ordst.writableBytes<length
readBytes(ByteBuf dst, int dstIndex, int length) ByteBuf 將當(dāng)前ByteBuf中的數(shù)據(jù)讀取到目標(biāo)ByteBuf (dst)中脊框,從readerIndex開(kāi)始讀取,長(zhǎng)度為length践啄,從目標(biāo)ByteBuf dstIndex開(kāi)始寫(xiě)入數(shù)據(jù)浇雹。讀取完成后,當(dāng)前ByteBuf的readerIndex+=length屿讽,目標(biāo)ByteBuf的writeIndex+=length throws IndexOutOfBoundsException:dstIndex<0 orthis.readableBytes<length ordst.capacity<dstIndex + length
readBytes(byte[] dst) ByteBuf 將當(dāng)前ByteBuf中的數(shù)據(jù)讀取到byte數(shù)組dst中昭灵,從當(dāng)前ByteBuf readerIndex開(kāi)始讀取,讀取長(zhǎng)度為dst.length伐谈,從byte數(shù)組dst索引0處開(kāi)始寫(xiě)入數(shù)據(jù)烂完。 throws IndexOutOfBoundsException:this.readableBytes<dst.length
readBytes(byte[] dst, int dstIndex, int length) ByteBuf 將當(dāng)前ByteBuf中的數(shù)據(jù)讀取到byte數(shù)組dst中,從當(dāng)前ByteBuf readerIndex開(kāi)始讀取诵棵,讀取長(zhǎng)度為length抠蚣,從byte數(shù)組dst索引dstIndex處開(kāi)始寫(xiě)入數(shù)據(jù)。 throws IndexOutOfBoundsException:dstIndex<0 or this.readableBytes<length or dst.length<dstIndex + length
readBytes(ByteBuffer dst) ByteBuf 將當(dāng)前ByteBuf中的數(shù)據(jù)讀取到ByteBuffer dst中履澳,從當(dāng)前ByteBuf readerIndex開(kāi)始讀取嘶窄,直到dst的位置指針到達(dá)ByteBuffer 的limit。讀取完成后距贷,當(dāng)前ByteBuf的readerIndex+=dst.remaining() throws IndexOutOfBoundsException:this.readableBytes<dst.remaining()
readBytes(OutputStream out, int length) ByteBuf 將當(dāng)前ByteBuf readerIndex讀取數(shù)據(jù)到輸出流OutputStream中柄冲,讀取的字節(jié)長(zhǎng)度為length throws IndexOutOfBoundsException:this.readableBytes<length throws IOException
readBytes(GatheringByteChannel out, int length) int 將當(dāng)前ByteBuf readerIndex讀取數(shù)到GatheringByteChannel 中,寫(xiě)入out的最大字節(jié)長(zhǎng)度為length忠蝗。GatheringByteChannel為非阻塞Channel现横,調(diào)用其write方法不能夠保存將全部需要寫(xiě)入的數(shù)據(jù)均寫(xiě)入成功,存在半包問(wèn)題阁最。因此其寫(xiě)入的數(shù)據(jù)長(zhǎng)度為【0戒祠,length】,如果操作成功速种,readerIndex+=實(shí)際寫(xiě)入的字節(jié)數(shù)得哆,返回實(shí)際寫(xiě)入的字節(jié)數(shù) throws IndexOutOfBoundsException:this.readableBytes<length throws IOException

1.4.2、順序?qū)懖僮鳎╳rite):

方法名稱 返回值 功能說(shuō)明 拋出異常
writeBoolean(boolean value) ByteBuf 將value寫(xiě)入到當(dāng)前ByteBuf中哟旗。寫(xiě)入成功贩据,writeIndex+=1 throws IndexOutOfBoundsException:this.writableBytes<1
writeByte(int value) ByteBuf 將value寫(xiě)入到當(dāng)前ByteBuf中。寫(xiě)入成功闸餐,writeIndex+=1 throws IndexOutOfBoundsException:this.writableBytes<1
writeShort(int value) ByteBuf 將value寫(xiě)入到當(dāng)前ByteBuf中饱亮。寫(xiě)入成功,writeIndex+=2 throws IndexOutOfBoundsException:this.writableBytes<2
writeMedium(int value) ByteBuf 將value寫(xiě)入到當(dāng)前ByteBuf中舍沙。寫(xiě)入成功近上,writeIndex+=3 throws IndexOutOfBoundsException:this.writableBytes<3
writeInt(int value) ByteBuf 將value寫(xiě)入到當(dāng)前ByteBuf中。寫(xiě)入成功拂铡,writeIndex+=4 throws IndexOutOfBoundsException:this.writableBytes<4
writeLong(long value) ByteBuf 將value寫(xiě)入到當(dāng)前ByteBuf中壹无。寫(xiě)入成功葱绒,writeIndex+=8 throws IndexOutOfBoundsException:this.writableBytes<8
writeChar(int value) ByteBuf 將value寫(xiě)入到當(dāng)前ByteBuf中。寫(xiě)入成功斗锭,writeIndex+=2 Throws IndexOutOfBoundsException:this.writableBytes<2
writeFloat(float value) ByteBuf 將value寫(xiě)入到當(dāng)前ByteBuf中地淀。寫(xiě)入成功,writeIndex+=4 throws IndexOutOfBoundsException:this.writableBytes<4
writeDouble(double value) ByteBuf 將value寫(xiě)入到當(dāng)前ByteBuf中岖是。寫(xiě)入成功帮毁,writeIndex+=8 throws IndexOutOfBoundsException:this.writableBytes<8
writeBytes(ByteBuf src) ByteBuf 將源ByteBuf src中從readerIndex開(kāi)始的所有可讀字節(jié)寫(xiě)入到當(dāng)前ByteBuf。從當(dāng)前ByteBuf writeIndex寫(xiě)入數(shù)據(jù)豺撑。寫(xiě)入成功烈疚,writeIndex+=src.readableBytes throws IndexOutOfBoundsException:this.writableBytes<src.readableBytes
writeBytes(ByteBuf src, int length) ByteBuf 將源ByteBuf src中從readerIndex開(kāi)始,長(zhǎng)度length的可讀字節(jié)寫(xiě)入到當(dāng)前ByteBuf聪轿。從當(dāng)前ByteBuf writeIndex寫(xiě)入數(shù)據(jù)爷肝。寫(xiě)入成功,writeIndex+=length throws IndexOutOfBoundsException:this.writableBytes<length or src.readableBytes<length
writeBytes(ByteBuf src, int srcIndex, int length) ByteBuf 將源ByteBuf src中從srcIndex開(kāi)始陆错,長(zhǎng)度length的可讀字節(jié)寫(xiě)入到當(dāng)前ByteBuf阶剑。從當(dāng)前ByteBuf writeIndex寫(xiě)入數(shù)據(jù)。寫(xiě)入成功危号,writeIndex+=length throws IndexOutOfBoundsException:srcIndex<0 or this.writableBytes<length or src.capacity<srcIndex + length
writeBytes(byte[] src) ByteBuf 將源字節(jié)數(shù)組src中所有可讀字節(jié)寫(xiě)入到當(dāng)前ByteBuf。從當(dāng)前ByteBuf writeIndex寫(xiě)入數(shù)據(jù)素邪。寫(xiě)入成功外莲,writeIndex+=src.length throws IndexOutOfBoundsException:this.writableBytes<src.length
writeBytes(byte[] src, int srcIndex, int length) ByteBuf 將源字節(jié)數(shù)組src中srcIndex開(kāi)始,長(zhǎng)度為length可讀字節(jié)寫(xiě)入到當(dāng)前ByteBuf兔朦。從當(dāng)前ByteBuf writeIndex寫(xiě)入數(shù)據(jù)偷线。寫(xiě)入成功,writeIndex+=length throws IndexOutOfBoundsException:srcIndex<0 or this.writableBytes<src.length or src.length<srcIndex + length
writeBytes(ByteBuffer mignsrc) ByteBuf 將源ByteBuffer src中所有可讀字節(jié)寫(xiě)入到當(dāng)前ByteBuf沽甥。從當(dāng)前ByteBuf writeIndex寫(xiě)入數(shù)據(jù)声邦。寫(xiě)入成功,writeIndex+=src.remaining() throws IndexOutOfBoundsException:this.writableBytes<src.remaining()
writeBytes(InputStream in, int length) int 將源InputStream in中的內(nèi)容寫(xiě)入到當(dāng)前ByteBuf摆舟,寫(xiě)入的最大長(zhǎng)度為length亥曹,實(shí)際寫(xiě)入的字節(jié)數(shù)可能少于length。從當(dāng)前ByteBuf writeIndex寫(xiě)入數(shù)據(jù)恨诱。寫(xiě)入成功媳瞪,writeIndex+=實(shí)際寫(xiě)入的字節(jié)數(shù)。返回實(shí)際寫(xiě)入的字節(jié)數(shù) throws IndexOutOfBoundsException:this.writableBytes<length
writeBytes(ScatteringByteChannel in, int length) int 將源ScatteringByteChannel in中的內(nèi)容寫(xiě)入到當(dāng)前ByteBuf照宝,寫(xiě)入的最大長(zhǎng)度為length蛇受,實(shí)際寫(xiě)入的字節(jié)數(shù)可能少于length。從當(dāng)前ByteBuf writeIndex寫(xiě)入數(shù)據(jù)厕鹃。寫(xiě)入成功兢仰,writeIndex+=實(shí)際寫(xiě)入的字節(jié)數(shù)乍丈。返回實(shí)際寫(xiě)入的字節(jié)數(shù) throws IndexOutOfBoundsException:this.writableBytes<length
writeZero(int length) ByteBuf 將當(dāng)前緩沖區(qū)的內(nèi)容填充為NUL(0x00),當(dāng)前ByteBuf writeIndex寫(xiě)入數(shù)據(jù)把将。寫(xiě)入成功轻专,writeIndex+=length throws IndexOutOfBoundsException: this.writableBytes<length

1.4.3、readerIndex 和 writeIndex

ByteBuf提供兩個(gè)指針用于支持順序讀寫(xiě)操作:readerIndex用于標(biāo)識(shí)讀取索引秸弛,witterIndex用于標(biāo)識(shí)寫(xiě)入索引铭若。兩個(gè)位置指針將ByteBuf緩沖區(qū)分割成三個(gè)區(qū)域。

緩沖區(qū)分段.png

作來(lái)重用這部分空間递览,以節(jié)約內(nèi)存叼屠,防止ByteBuf動(dòng)態(tài)擴(kuò)張。這在私有協(xié)議棧消息解碼是非常有用绞铃,因TCP底層粘包镜雨,多個(gè)整包消息被TCP粘包作物一個(gè)整包發(fā)送。通過(guò)discardReadBytes()操作可以重用之前已經(jīng)解碼過(guò)的緩沖區(qū)儿捧,從而防止接收緩沖區(qū)因容量不足導(dǎo)致的擴(kuò)展荚坞。但discardReadBytes()操作是把雙刃劍,不能濫用菲盾。

方法名稱 返回值 功能說(shuō)明 拋出異常
readerIndex() int 返回當(dāng)前ByteBuf的readerIndex
readerIndex(int readerIndex) ByteBuf 修改當(dāng)前ByteBuf的readerIndex throws IndexOutOfBoundsException this.writerIndex<readerIndex
writerIndex() int 返回當(dāng)前ByteBuf的writeIndex
writerIndex(int writerIndex) ByteBuf 修改當(dāng)前ByteBuf的writeIndex throws IndexOutOfBoundsException writeIndex<this.readerIndex or this.capacity<writerIndex
readableBytes() int 獲取當(dāng)前ByteBuf的可讀字節(jié)數(shù) this.writerIndex -this.readerIndex
writableBytes() int 獲取當(dāng)前ByteBuf的可寫(xiě)字節(jié)數(shù) this.capacity - this.writerIndex
setIndex(int readerIndex, int writerIndex) ByteBuf 快捷設(shè)置當(dāng)前ByteBuf的readerIndex和writerIndex throws IndexOutOfBoundsException readerIndex<0 or this.writerIndex<readerIndex or this.capacity<writerIndex
skipBytes(int length) ByteBuf 更新當(dāng)前ByteBuf的readerIndex颓影,更新后將跳過(guò)length字節(jié)的數(shù)據(jù)讀取。 throws IndexOutOfBoundsException this.readableBytes<length

1.4.4懒鉴、discardReadBytes()和clear操作

相比其他操作诡挂,緩沖區(qū)分配和釋放是比較耗時(shí)的操作,因此临谱,我們需要盡量重用它們璃俗。而緩沖區(qū)的動(dòng)態(tài)擴(kuò)張需要字節(jié)數(shù)組的復(fù)制,其是個(gè)耗時(shí)的操作悉默。因此為最大程度提升性能城豁,需要盡最大努力提升緩沖區(qū)的重用率。

例如:
緩沖區(qū)包含N個(gè)整包消息抄课,每個(gè)消息長(zhǎng)度L唱星,緩沖區(qū)可寫(xiě)字節(jié)數(shù)為R。當(dāng)讀取M個(gè)整包消息后跟磨,如果不對(duì)ByteBuf做壓縮或discardReadBytes()操作魏颓,則可寫(xiě)緩沖區(qū)長(zhǎng)度依然為R。如果做discardReadBytes()操作吱晒,則可寫(xiě)字節(jié)數(shù)變?yōu)镽=(R+M*L)甸饱,之前已經(jīng)讀取的M個(gè)整包的空間會(huì)被重用。

需要注意的是,discardReadBytes()操作會(huì)發(fā)生字節(jié)數(shù)組的內(nèi)存復(fù)制叹话,故頻繁調(diào)用會(huì)導(dǎo)致性能下降偷遗,因此要注意調(diào)用場(chǎng)景。

至于clear()操作驼壶,正如JDK的ByteBuffer的clear()一樣氏豌,它并不會(huì)清空緩沖區(qū)內(nèi)容本身。其主要通過(guò)還原readerIndex和writterIndex的值來(lái)達(dá)到清空緩沖區(qū)的效果热凹。

方法名稱 返回值 功能說(shuō)明
discardReadBytes() ByteBuf 釋放0到readerIndex之間已經(jīng)讀取的空間泵喘;同時(shí)復(fù)制readerIndex和writerIndex之間的數(shù)據(jù)到0到writerIndex-readerIndex之間;修改readerIndex和writerIndex的值般妙。該操作會(huì)發(fā)生字節(jié)數(shù)據(jù)的內(nèi)存復(fù)制纪铺,頻繁調(diào)用會(huì)導(dǎo)致性能下降。此外碟渺,相比其他java對(duì)象鲜锚,緩沖區(qū)的分配和釋放是個(gè)耗時(shí)的操作,緩沖區(qū)的動(dòng)態(tài)擴(kuò)張需要進(jìn)行進(jìn)行字節(jié)數(shù)據(jù)的復(fù)制苫拍,也是耗時(shí)的操作芜繁,因此應(yīng)盡量提高緩沖區(qū)的重用率
discardSomeReadBytes() ByteBuf 功能和discardReadBytes()相似,不同之處在于可定制要釋放的空間绒极,依賴于具體實(shí)現(xiàn)
clear() ByteBuf 與JDK 的ByteBuffer clear操作相同骏令,該操作不會(huì)清空緩沖區(qū)內(nèi)容本身,其主要是為了操作位置指針垄提,將readerIndex和writerIndex重置為0

1.4.5榔袋、Readable bytes和Writable bytes

readable bytes區(qū)段是數(shù)據(jù)的實(shí)際存儲(chǔ)區(qū)域,已read和skip開(kāi)頭的任何操作都將從readIndex開(kāi)始讀取或者跳過(guò)指定的數(shù)據(jù)塔淤,操作完成之后readIndex增加了讀取或跳過(guò)的字節(jié)數(shù)長(zhǎng)度。若讀取字節(jié)數(shù)長(zhǎng)度大于實(shí)際可讀的字節(jié)數(shù)速妖,則拋出IndexOutOfBoundException高蜂。當(dāng)出現(xiàn)分配、包裝或復(fù)制一個(gè)新的ByteBuf對(duì)象時(shí)罕容,它的readerIndex為0备恤。

writable bytes區(qū)段是尚未被使用的空閑空間,任何write開(kāi)頭的操作都會(huì)從writerIndex開(kāi)始向空閑空間寫(xiě)入锦秒,操作完成后writeIndex增加寫(xiě)入的字節(jié)長(zhǎng)度露泊。如果寫(xiě)入字節(jié)長(zhǎng)度大于可寫(xiě)的字節(jié)數(shù),則拋出IndexOutOfBoundException異常旅择。新分配惭笑、包裝器或復(fù)制一個(gè)ByteBuf時(shí),其writterIndex為0。

方法名稱 返回值 功能說(shuō)明
readableBytes() int 獲取可讀的字節(jié)數(shù)沉噩,其等效于(writerIndex - readerIndex)
writableBytes() int 獲取可寫(xiě)的字節(jié)數(shù)捺宗,其等效于(capacity - waiterIndex)
maxWritableBytes() int 獲取最大可讀取字節(jié)數(shù),其等效于(maxCapacity - writterIndex)

1.4.6川蒙、Mark和Reset

當(dāng)對(duì)緩沖區(qū)進(jìn)行讀寫(xiě)操作時(shí)蚜厉,可能需要對(duì)之前的操作進(jìn)行回滾。ByteBuf可通過(guò)調(diào)用mark操作將當(dāng)前的位置指針備份到mark變量中畜眨,調(diào)用rest操作后昼牛,重新將指針的當(dāng)前位置恢復(fù)為備份在mark變量的值。ByteBuf主要有以下相關(guān)方法:

  • markReaderIndex():將當(dāng)前的readerIndex備份到markedReaderIndex中康聂;
  • resetReaderIndex():將當(dāng)前的readerIndex重置為markedReaderIndex的值贰健;
  • markWriterIndex() :將當(dāng)前的writerIndex備份到markedWriterIndex中;
  • resetWriterIndex():將當(dāng)前的writerIndex重置為markedWriterIndex的值早抠;

1.4.7霎烙、查找操作

ByteBuf提供多種查找方法用于滿足不同應(yīng)用場(chǎng)景,細(xì)分如下:

方法名稱 返回值 功能說(shuō)明 拋出異常
indexOf(int fromIndex, int toIndex, byte value) int 從當(dāng)前ByteBuf中查找首次出現(xiàn)value的位置蕊连,fromIndex<=查找范圍<toIndex悬垃;查找成功返回位置索引,否則返回-1
bytesBefore(byte value) int 從當(dāng)前ByteBuf中查找首次出現(xiàn)value的位置甘苍,readerIndex<=查找范圍<writerIndex尝蠕;查找成功返回位置索引,否則返回-1
bytesBefore(int length, byte value) int 從當(dāng)前ByteBuf中查找首次出現(xiàn)value的位置载庭,readerIndex<=查找范圍<readerIndex+length覆糟;查找成功返回位置索引,否則返回-1 IndexOutOfBoundsException:this.readableBytes<length
bytesBefore(int index, int length, byte value) int 從當(dāng)前ByteBuf中查找首次出現(xiàn)value的位置膝宁,index<=查找范圍<index+length蓖谢;查找成功返回位置索引,否則返回-1 IndexOutOfBoundsException:this.readableBytes<index+length
forEachByte(ByteBufProcessor processor); int 遍歷當(dāng)前ByteBuf的可讀字節(jié)數(shù)組顽铸,與ByteBufProcessor中設(shè)置的查找條件進(jìn)行比對(duì)茁计,從readerIndex開(kāi)始遍歷直到writerIndex。如果滿足條件谓松,返回位置索引星压,否則返回-1
forEachByte(int index, int length, ByteBufProcessor processor) void 遍歷當(dāng)前ByteBuf的可讀字節(jié)數(shù)組,與ByteBufProcessor中設(shè)置的查找條件進(jìn)行比對(duì)鬼譬,從index開(kāi)始遍歷直到index+length娜膘。如果滿足條件,返回位置索引优质,否則返回-1
forEachByteDesc(ByteBufProcessor processor) 逆序遍歷當(dāng)前ByteBuf的可讀字節(jié)數(shù)組竣贪,與ByteBufProcessor中設(shè)置的查找條件進(jìn)行比對(duì)军洼,從writerIndex-1開(kāi)始遍歷直到readerIndex。如果滿足條件贾富,返回位置索引歉眷,否則返回-1
forEachByteDesc(int index, int length, ByteBufProcessor processor) 逆序遍歷當(dāng)前ByteBuf的可讀字節(jié)數(shù)組,與ByteBufProcessor中設(shè)置的查找條件進(jìn)行比對(duì)颤枪,從index+length-1開(kāi)始遍歷直到index汗捡。如果滿足條件,返回位置索引畏纲,否則返回-1

對(duì)應(yīng)查找字節(jié)扇住,存在一些常用值,如回車(chē)換行等盗胀,Netty在ByteBufProcessor接口中對(duì)這些常用查找字符進(jìn)行了抽象:

ByteBufProcessor 功能說(shuō)明
FIND_NUL NUL(0x00)艘蹋,查找是否為空字節(jié)
FIND_NON_NUL 查找是否為非空字節(jié)
FIND_CR CR('\r'),查找是否為回車(chē)符
FIND_NON_CR 查找是否為非回車(chē)符
FIND_LF LF('\n')票灰,查找是否為換行符
FIND_NON_LF 查找是否為非換行符
FIND_CRLF CR('\r')或LF('\n')女阀,回車(chē)或換行
FIND_NON_CRLF
FIND_LINEAR_WHITESPACE ' '或'\t',查找是否為空字符或換行符
FIND_NON_LINEAR_WHITESPACE

1.4.8屑迂、Buffer視圖操作

Derived Buffers類似于數(shù)據(jù)庫(kù)視圖浸策,ByteBuf提供了多個(gè)接口用于創(chuàng)建某個(gè)ByteBuf的視圖或者復(fù)制ByteBuf。

主要操作如下:

方法名稱 返回值 功能說(shuō)明
duplicate() ByteBuf 返回當(dāng)前ByteBuf的復(fù)制對(duì)象惹盼,復(fù)制后的ByteBuf對(duì)象與當(dāng)前ByteBuf對(duì)象共享緩沖區(qū)的內(nèi)容庸汗,但是維護(hù)自己獨(dú)立的readerIndex和writerIndex。該操作不修改原ByteBuf的readerIndex和writerIndex手报。
copy() ByteBuf 從當(dāng)前ByteBuf復(fù)制一個(gè)新的ByteBuf對(duì)象蚯舱,復(fù)制的新對(duì)象緩沖區(qū)的內(nèi)容和索引均是獨(dú)立的。該操作不修改原ByteBuf的readerIndex和writerIndex掩蛤。(復(fù)制readerIndex到writerIndex之間的內(nèi)容枉昏,其他屬性與原ByteBuf相同,如maxCapacity揍鸟,ByteBufAllocator)
copy(int index, int length) ByteBuf 從當(dāng)前ByteBuf 指定索引index開(kāi)始兄裂,字節(jié)長(zhǎng)度為length,復(fù)制一個(gè)新的ByteBuf對(duì)象蜈亩,復(fù)制的新對(duì)象緩沖區(qū)的內(nèi)容和索引均是獨(dú)立的懦窘。該操作不修改原ByteBuf的readerIndex和writerIndex前翎。(其他屬性與原ByteBuf相同稚配,,如maxCapacity港华,ByteBufAllocator)
slice() ByteBuf 返回當(dāng)前ByteBuf的可讀子區(qū)域道川,起始位置從readerIndex到writerIndex,返回的ByteBuf對(duì)象與當(dāng)前ByteBuf對(duì)象共享緩沖區(qū)的內(nèi)容,但是維護(hù)自己獨(dú)立的readerIndex和writerIndex冒萄。該操作不修改原ByteBuf的readerIndex和writerIndex臊岸。返回ByteBuf對(duì)象的長(zhǎng)度為readableBytes()
slice(int index, int length) ByteBuf 返回當(dāng)前ByteBuf的可讀子區(qū)域,起始位置從index到index+length尊流,返回的ByteBuf對(duì)象與當(dāng)前ByteBuf對(duì)象共享緩沖區(qū)的內(nèi)容帅戒,但是維護(hù)自己獨(dú)立的readerIndex和writerIndex。該操作不修改原ByteBuf的readerIndex和writerIndex崖技。返回ByteBuf對(duì)象的長(zhǎng)度為length

1.4.9逻住、轉(zhuǎn)換為JDK ByteBuffer

當(dāng)通過(guò)NIO的SocketChannel進(jìn)行網(wǎng)絡(luò)讀寫(xiě)時(shí),操作的對(duì)象為JDK的ByteBuffer迎献,因此須在接口層支持netty ByteBuf到JDK的ByteBuffer的相互轉(zhuǎn)換瞎访。

將ByteBuf轉(zhuǎn)換為java.nio.ByteBuffer的方法主要有如下兩個(gè):

方法名稱 返回值 功能說(shuō)明 拋出異常
nioBuffer() ByteBuffer 將當(dāng)前ByteBuf的可讀緩沖區(qū)(readerIndex到writerIndex之間的內(nèi)容)轉(zhuǎn)換為ByteBuffer,兩者共享共享緩沖區(qū)的內(nèi)容吁恍。對(duì)ByteBuffer的讀寫(xiě)操作不會(huì)影響B(tài)yteBuf的讀寫(xiě)索引扒秸。注意:ByteBuffer無(wú)法感知ByteBuf的動(dòng)態(tài)擴(kuò)展操作。ByteBuffer的長(zhǎng)度為readableBytes() UnsupportedOperationException
nioBuffer(int index, int length) ByteBuffer 將當(dāng)前ByteBuf的可讀緩沖區(qū)(index到index+length之間的內(nèi)容)轉(zhuǎn)換為ByteBuffer冀瓦,兩者共享共享緩沖區(qū)的內(nèi)容伴奥。對(duì)ByteBuffer的讀寫(xiě)操作不會(huì)影響B(tài)yteBuf的讀寫(xiě)索引。注意:ByteBuffer無(wú)法感知ByteBuf的動(dòng)態(tài)擴(kuò)展操作咕幻。ByteBuffer的長(zhǎng)度為length UnsupportedOperationException

1.4.10渔伯、隨機(jī)讀寫(xiě)(set和get)

除順序讀寫(xiě)之外,ByteBuf還支持隨機(jī)讀寫(xiě)肄程,其最大的區(qū)別在于可隨機(jī)指定讀寫(xiě)的索引位置锣吼。關(guān)于隨機(jī)讀寫(xiě)的API這里不再詳述。無(wú)論set或get蓝厌,執(zhí)行前都會(huì)進(jìn)行索引和長(zhǎng)度的合法性驗(yàn)證玄叠,此外,set操作不同于write操作拓提,set不支持動(dòng)態(tài)擴(kuò)展读恃,所以使用者必須保證當(dāng)前緩沖區(qū)可寫(xiě)的字節(jié)數(shù)大于需要寫(xiě)入的字節(jié)長(zhǎng)度,否則會(huì)拋出緩沖區(qū)越界異常代态。

隨機(jī)讀操作:

隨機(jī)讀操作.png

隨機(jī)寫(xiě)操作:

隨機(jī)寫(xiě)操作.png

2寺惫、ByteBuf實(shí)現(xiàn)類分析

2.1、ByteBuf類繼承圖

ByteBuf類繼承圖.png
  • ReferenceCounted:對(duì)象引用計(jì)數(shù)器蹦疑,初始化ReferenceCounted對(duì)象時(shí)西雀,引用數(shù)量refCnt為1,調(diào)用retain()可增加refCnt歉摧,release()用于減少refCnt艇肴。refCnt為1時(shí)腔呜,說(shuō)明對(duì)象實(shí)際不可達(dá),release()方法將立即調(diào)用deallocate()釋放對(duì)象再悼。如果refCnt為0核畴,說(shuō)明對(duì)象被錯(cuò)誤的引用。在AbstractReferenceCountedByteBuf源碼分析小節(jié)將詳細(xì)介紹ReferenceCounted的原理冲九。
  • ByteBuf:實(shí)現(xiàn)接口ReferenceCounted和Comparable谤草,實(shí)現(xiàn)ReferenceCounted使得ByteBuf具備引用計(jì)數(shù)的能力,方便跟蹤ByteBuf對(duì)象分配和釋放莺奸。

ByteBuf直接子類:

  • EmptyByteBuf:用于構(gòu)建空ByteBuf對(duì)象咖刃,capacity和maxCapacity均為0。
  • WrappedByteBuf:用于裝飾ByteBuf對(duì)象憾筏,主要有AdvancedLeakAwareByteBuf嚎杨、SimpleLeakAwareByteBuf、UnreleasableByteBuf和Component四個(gè)子類氧腰。這里WrappedByteBuf使用裝飾者模式裝飾ByteBuf對(duì)象枫浙,AdvancedLeakAwareByteBuf用于對(duì)所有操作記錄堆棧信息,方便監(jiān)控內(nèi)存泄漏古拴;SimpleLeakAwareByteBuf只記錄order(ByteOrder endianness)的堆棧信息箩帚;UnreleasableByteBuf用于阻止修改對(duì)象引用計(jì)數(shù)器refCnt的值。
  • AbstractByteBuf:提供ByteBuf的默認(rèn)實(shí)現(xiàn)黄痪,同時(shí)組合ResourceLeakDetector和SwappedByteBuf的能力紧帕,ResourceLeakDetector是內(nèi)存泄漏檢測(cè)工具,SwappedByteBuf用于字節(jié)序不同時(shí)轉(zhuǎn)換字節(jié)序桅打。

AbstractReferenceCountedByteBuf直接子類:

  • CompositeByteBuf:用于將多個(gè)ByteBuf組合在一起是嗜,形成一個(gè)虛擬的ByteBuf對(duì)象,支持讀寫(xiě)和動(dòng)態(tài)擴(kuò)展挺尾。內(nèi)部使用List<Component>組合多個(gè)ByteBuf鹅搪。推薦使用ByteBufAllocator的compositeBuffer()方法,Unpooled的工廠方法compositeBuffer()或wrappedBuffer(ByteBuf... buffers)創(chuàng)建CompositeByteBuf對(duì)象遭铺。
  • FixedCompositeByteBuf:用于將多個(gè)ByteBuf組合在一起丽柿,形成一個(gè)虛擬的只讀ByteBuf對(duì)象,不允許寫(xiě)入和動(dòng)態(tài)擴(kuò)展魂挂。內(nèi)部使用Object[]將多個(gè)ByteBuf組合在一起甫题,一旦FixedCompositeByteBuf對(duì)象構(gòu)建完成,則不會(huì)被更改涂召。
  • PooledByteBuf<T>:基于內(nèi)存池的ByteBuf坠非,主要為了重用ByteBuf對(duì)象,提升內(nèi)存的使用效率芹扭;適用于高負(fù)載麻顶,高并發(fā)的應(yīng)用中。主要有PooledDirectByteBuf舱卡,PooledHeapByteBuf辅肾,PooledUnsafeDirectByteBuf三個(gè)子類,PooledDirectByteBuf是在堆外進(jìn)行內(nèi)存分配的內(nèi)存池ByteBuf轮锥,PooledHeapByteBuf是基于堆內(nèi)存分配內(nèi)存池ByteBuf矫钓,PooledUnsafeDirectByteBuf也是在堆外進(jìn)行內(nèi)存分配的內(nèi)存池ByteBuf,區(qū)別在于PooledUnsafeDirectByteBuf內(nèi)部使用基于PlatformDependent相關(guān)操作實(shí)現(xiàn)ByteBuf舍杜,具有平臺(tái)相關(guān)性新娜。
  • ReadOnlyByteBufferBuf:只讀ByteBuf,內(nèi)部持有ByteBuffer對(duì)象既绩,相關(guān)操作委托給ByteBuffer實(shí)現(xiàn)概龄,該ByteBuf限內(nèi)部使用,ReadOnlyByteBufferBuf還有一個(gè)子類ReadOnlyUnsafeDirectByteBuf饲握。
  • UnpooledDirectByteBuf:在堆外進(jìn)行內(nèi)存分配的非內(nèi)存池ByteBuf私杜,內(nèi)部持有ByteBuffer對(duì)象,相關(guān)操作委托給ByteBuffer實(shí)現(xiàn)救欧。
  • UnpooledHeapByteBuf:基于堆內(nèi)存分配非內(nèi)存池ByteBuf衰粹,即內(nèi)部持有byte數(shù)組。
  • UnpooledUnsafeDirectByteBuf:與UnpooledDirectByteBuf相同笆怠,區(qū)別在于UnpooledUnsafeDirectByteBuf內(nèi)部使用基于PlatformDependent相關(guān)操作實(shí)現(xiàn)ByteBuf铝耻,具有平臺(tái)相關(guān)性。

從內(nèi)存分配角度看蹬刷,ByteBuf主要分為兩類:

  • 堆內(nèi)存(HeapByteBuf)字節(jié)緩沖區(qū):特點(diǎn)是內(nèi)存的分配和回收速度快瓢捉,可以被JVM自動(dòng)回收;缺點(diǎn)是進(jìn)行Socket的I/O讀寫(xiě)需要額外進(jìn)行一次內(nèi)存復(fù)制办成,即將內(nèi)存對(duì)應(yīng)的緩沖區(qū)復(fù)制到內(nèi)核Channel中泊柬,性能會(huì)有一定程度下降。
  • 直接內(nèi)存(DirectByteBuf)字節(jié)緩沖區(qū):在堆外進(jìn)行內(nèi)存分配诈火,相比堆內(nèi)存兽赁,分配和回收速度稍慢。但用于Socket的I/O讀寫(xiě)時(shí)冷守,少一次內(nèi)存復(fù)制刀崖,速度比堆內(nèi)存字節(jié)緩沖區(qū)快。

經(jīng)驗(yàn)表明拍摇,在I/O通信線程的讀寫(xiě)緩沖區(qū)使用DirectByteBuf亮钦,后端業(yè)務(wù)消息的編解碼模塊使用HeapByteBuf,這樣組合可以達(dá)到性能最優(yōu)充活。

從內(nèi)存回收角度看蜂莉,ByteBuf也分為兩類:

  • 基于內(nèi)存池的ByteBuf:優(yōu)點(diǎn)是可以重用ByteBuf對(duì)象蜡娶,通過(guò)自己維護(hù)一個(gè)內(nèi)存池,可以循環(huán)利用創(chuàng)建的ByteBuf映穗,提升內(nèi)存的使用效率窖张,降低由于高負(fù)載導(dǎo)致的頻繁GC。適用于高負(fù)載蚁滋,高并發(fā)的應(yīng)用中宿接。推薦使用基于內(nèi)存池的ByteBuf。
  • 非內(nèi)存池的ByteBuf:優(yōu)點(diǎn)是管理和維護(hù)相對(duì)簡(jiǎn)單辕录。

盡管推薦使用基于內(nèi)存池的ByteBuf睦霎,但內(nèi)存池的管理和維護(hù)更加復(fù)雜,使用起來(lái)也需要更加謹(jǐn)慎走诞。

2.2副女、AbstractByteBuf源碼分析

AbstractByteBuf是ByteBuf的抽象實(shí)現(xiàn),其提供了一些基礎(chǔ)屬性和公用方法實(shí)現(xiàn)蚣旱。

2.2.1肮塞、主要成員變量

readerIndex:讀索引
writerIndex:寫(xiě)索引
markedReaderIndex:讀索引標(biāo)記
markedWriterIndex:寫(xiě)索引標(biāo)記
maxCapacity:最大容量
SwappedByteBuf swappedBuf:緩沖區(qū)包裝類

在AbstractByteBuf中并未定義ByteBuf緩沖區(qū)的實(shí)現(xiàn),其實(shí)現(xiàn)留給具體實(shí)現(xiàn)類姻锁。

2.2.2枕赵、讀操作簇

AbstractByteBuf讀操作簇.png

在讀取數(shù)據(jù)數(shù)據(jù)前,會(huì)對(duì)緩沖區(qū)可讀空間進(jìn)行檢驗(yàn)位隶,如果讀取長(zhǎng)度小于0拷窜,則拋出IllegalArgumentException異常提示參數(shù)非法;如果需要讀取的字節(jié)數(shù)大于可讀長(zhǎng)度涧黄,則拋出IndexOutOfBoundsException異常篮昧。異常中封裝了詳細(xì)信息,使用者可以方便地定位問(wèn)題笋妥。

校驗(yàn)通過(guò)后在讀取設(shè)定長(zhǎng)度的數(shù)據(jù)懊昨,其具體實(shí)現(xiàn)依賴于子類實(shí)現(xiàn)。

2.2.3春宣、寫(xiě)操作簇

AbstractByteBuf寫(xiě)操作簇.png
  • 首先校驗(yàn)寫(xiě)入長(zhǎng)度的合法性酵颁;

  • 如果寫(xiě)入的數(shù)據(jù)長(zhǎng)度小于0,則拋出IllegalArgumentException異常月帝;如果寫(xiě)入長(zhǎng)度大于可動(dòng)態(tài)擴(kuò)展的最大長(zhǎng)度躏惋,則拋出IndexOutOfBoundsException異常;

  • 當(dāng)寫(xiě)入長(zhǎng)度大于緩沖區(qū)長(zhǎng)度而小于可動(dòng)態(tài)擴(kuò)展的最大長(zhǎng)度嚷辅,則進(jìn)行動(dòng)態(tài)擴(kuò)展簿姨;動(dòng)態(tài)擴(kuò)展根據(jù)倍增加閥值的方式;

2.2.4、操作索引

AbstractByteBuf操作索引.png

設(shè)置和讀取相關(guān)索引扁位;

2.2.4准潭、重用緩沖區(qū)

重用緩沖區(qū)是通過(guò)discardReadBytes()和discardSomeReadBytes()方法實(shí)現(xiàn)。

處理流程如下:

  • 如果讀索引為0域仇,表示無(wú)可重用緩沖區(qū)刑然,則直接返回;
  • 如果讀索引大于0且不等于寫(xiě)索引殉簸,說(shuō)明緩沖區(qū)有數(shù)據(jù)且有可重用空間,調(diào)用setBytes(0, this, readerIndex, writerIndex-readerIndex)方法進(jìn)行緩沖區(qū)復(fù)制沽讹,然后重新設(shè)置讀索引為0般卑,寫(xiě)索引為(writerIndex - readerIndex);

2.2.5爽雄、skipBytes

在解碼時(shí)蝠检,有時(shí)需要丟棄非法數(shù)據(jù),使用skipBytes()可方便跳過(guò)指定長(zhǎng)度的數(shù)據(jù)挚瘟。

2.3叹谁、AbstractReferenceCountedByteBuf

AbstractReferenceCountedByteBuf主要是對(duì)引用進(jìn)行計(jì)數(shù),類似JVM內(nèi)存回收的對(duì)象引用計(jì)數(shù)乘盖,用于跟蹤對(duì)象的分配和銷毀焰檩。

2.3.1、成員變量

refCntUpdater:類型為AtomicIntegerFieldUpdater订框,通過(guò)原子方式對(duì)成員變量進(jìn)行更新等操作析苫,以實(shí)現(xiàn)線程安全,消除鎖穿扳;

2.3.1衩侥、對(duì)象引用計(jì)數(shù)

retain()方法用于引用計(jì)數(shù)加一,由于可能存在并發(fā)場(chǎng)景矛物,所以其通過(guò)自旋加CAS操作保證線程安全茫死。

實(shí)現(xiàn)源碼如下:

private ByteBuf retain0(final int increment) {
  int oldRef = refCntUpdater.getAndAdd(this, increment);
  if (oldRef <= 0 || oldRef + increment < oldRef) {
    // Ensure we don't resurrect (which means the refCnt was 0) and also that we encountered an overflow.
    refCntUpdater.getAndAdd(this, -increment);
    throw new IllegalReferenceCountException(oldRef, increment);
  }
  return this;
}

2.4、UnpooledHeapByteBuf源碼解析

UnpooledHeapByteBuf是基于堆內(nèi)存進(jìn)行內(nèi)存分配的字節(jié)緩沖區(qū)履羞,沒(méi)有基于對(duì)象池技術(shù)實(shí)現(xiàn)峦萎,每次調(diào)用都會(huì)創(chuàng)建一個(gè)新的對(duì)象;

2.4.1忆首、成員變量

成員變量中定義了一個(gè)ByteBufAllocator類型的alloc用來(lái)分配堆內(nèi)存骨杂,定義一個(gè)byte字節(jié)數(shù)組用作緩沖區(qū),定義一個(gè)ByteBuffer類型的tmpNioBuf用作Netty的ByteBuf到JDK ByteBuffer轉(zhuǎn)換雄卷;

private final ByteBufAllocator alloc;
byte[] array;
private ByteBuffer tmpNioBuf;

實(shí)際使用ByteBuffer作為緩沖區(qū)也是可行搓蚪,使用Byte數(shù)組根本原因是提升性能和更加敏捷地進(jìn)行位操作。

2.4.2丁鹉、動(dòng)態(tài)擴(kuò)展緩沖區(qū)

擴(kuò)容源碼如下:

    @Override
    public ByteBuf capacity(int newCapacity) {
        checkNewCapacity(newCapacity);

        int oldCapacity = array.length;
        byte[] oldArray = array;
        if (newCapacity > oldCapacity) {
            byte[] newArray = allocateArray(newCapacity);
            System.arraycopy(oldArray, 0, newArray, 0, oldArray.length);
            setArray(newArray);
            freeArray(oldArray);
        } else if (newCapacity < oldCapacity) {
            byte[] newArray = allocateArray(newCapacity);
            int readerIndex = readerIndex();
            if (readerIndex < newCapacity) {
                int writerIndex = writerIndex();
                if (writerIndex > newCapacity) {
                    writerIndex(writerIndex = newCapacity);
                }
                System.arraycopy(oldArray, readerIndex, newArray, readerIndex, writerIndex - readerIndex);
            } else {
                setIndex(newCapacity, newCapacity);
            }
            setArray(newArray);
            freeArray(oldArray);
        }
        return this;
    }

先判斷新的容量是否大于當(dāng)前緩沖區(qū)的容量妒潭,如果大于則進(jìn)行動(dòng)態(tài)擴(kuò)容悴能;通過(guò)byte[] newArray = new byte[newCapacity]創(chuàng)建新容量的字節(jié)數(shù)組,然后通過(guò)System.arrayCopy()方法進(jìn)行數(shù)據(jù)復(fù)制雳灾,最后通過(guò)setArray()方法替換舊的緩沖區(qū)數(shù)組漠酿;

2.5、PooledByteBuf源碼解析

PooledByteBuf是基于內(nèi)存池的緩沖區(qū)谎亩,netty自己實(shí)現(xiàn)了一套內(nèi)存池管理炒嘲,具體實(shí)現(xiàn)類有:PooledHeapByteBuf、PooledDirectByteBuf匈庭、PooledUnsafeDirectByteBuf夫凸、PooledUnsafeHeapByteBuf。

由于內(nèi)存池管理等方面涉及內(nèi)容較多阱持,本處不多贅述夭拌,詳情請(qǐng)看Netty內(nèi)存池相關(guān)文章:

推薦博客:http://www.reibang.com/p/4856bd30dd56

3、ByteBuf相關(guān)輔助類

netty提供一些ByteBuf相關(guān)的輔助類衷咽,以簡(jiǎn)化緩沖區(qū)相關(guān)的使用鸽扁;

3.1、ByteBufHolder

ByteBufHolder是ByteBuf的容器镶骗,在Netty中桶现,它非常有用,例如HTTP協(xié)議的請(qǐng)求消息和應(yīng)答消息都可以攜帶消息體鼎姊,這個(gè)消息體在NIO ByteBuffer中就是個(gè)ByteBuffer對(duì)象巩那,在Netty中就是ByteBuf對(duì)象。由于不同的協(xié)議消息體可以包含不同的協(xié)議字段和功能此蜈,因此即横,需要對(duì)ByteBuf進(jìn)行包裝和抽象,不同的子類可以有不同的實(shí)現(xiàn)裆赵。為了滿足這些定制化的需求东囚,Netty抽象出了ByteBufHolder對(duì)象,它包含了一個(gè)ByteBuf战授,另外還提供了一些其他實(shí)用的方法页藻,使用者繼承ByteBufHolder接口后可以按需封裝自己的實(shí)現(xiàn)。

3.1植兰、ByteBufAllocator

ByteBufAllocator是字節(jié)緩沖區(qū)分配器份帐,按照Netty的緩沖區(qū)實(shí)現(xiàn)不同,共有兩種不同的分配器:基于內(nèi)存池的字節(jié)緩沖區(qū)分配器和普通的字節(jié)緩沖區(qū)分配器楣导。

類繼承圖如下:

ByteBufAllocator類繼承圖.png

主要功能列表:

方法名稱 返回值 功能說(shuō)明
buffer() ByteBuf 分配一個(gè)字節(jié)緩沖區(qū)废境,緩沖區(qū)的類型由ByteBufAllocator的實(shí)現(xiàn)決定
buffer(int initialCapacity) ByteBuf 分配一個(gè)初始容量initialCapacity的字節(jié)緩沖區(qū),緩沖區(qū)的類型由ByteBufAllocator的實(shí)現(xiàn)類決定
buffer(int initialCapacity,int maxCapacity) ByteBuf 分配一個(gè)初始容量initialCapacity噩凹,最大容量為maxCapacity的字節(jié)緩沖區(qū)巴元,緩沖區(qū)的類型由ByteBufAllocator的實(shí)現(xiàn)類決定
ioBuffer(int initialCapacity,int maxCapacity) ByteBuf 分配一個(gè)初始容量initialCapacity驮宴,最大容量為maxCapacity的direct buffer
heapBuffer(int initialCapacity逮刨,int maxCapacity) ByteBuf 分配一個(gè)初始容量initialCapacity,最大容量為maxCapacity的heap buffer
directBuffer(int initialCapacity堵泽,int maxCapacity) ByteBuf 分配一個(gè)初始容量initialCapacity修己,最大容量為maxCapacity的directbuffer
compositeBuffer(int maxNumComponent) CompositeByteBuf 分配一個(gè)最大容量為maxCapacity的CompositeByteBuf,內(nèi)存類型由ByteBufAllocator的實(shí)現(xiàn)類決定
isDirectBufferPooled() boolean 是否使用了直接內(nèi)存內(nèi)存池

3.2迎罗、CompositeByteBuf

CompositeBytebuf允許將多個(gè)Bytebuf的實(shí)例組合起來(lái)睬愤,在內(nèi)部使用一個(gè)Bytebuf進(jìn)行統(tǒng)一管理。

CompositeBytebuf在某些場(chǎng)景非常有用佳谦,例如在協(xié)議中一個(gè)協(xié)議通常會(huì)包含多個(gè)部分戴涝,消息體滋戳,消息頭等钻蔑,他們都是Bytebuf對(duì)象,如果我們需要對(duì)他進(jìn)行管理奸鸯,可以把這些Bytebuf對(duì)象都放到CompositeBytebuf中進(jìn)行統(tǒng)一管理

成員變量:

private final ByteBufAllocator alloc;
private final boolean direct;
private final ComponentList components;
private final int maxNumComponents;
private boolean freed;

CompositeBytebuf定義了一個(gè)Component類型的集合咪笑,Component為ByteBuf的包裝器實(shí)現(xiàn)類,其聚合了ByteBuf對(duì)象娄涩,維護(hù)了在集合中的位置偏移量信息等窗怒;

Component源碼實(shí)現(xiàn):

private static final class Component {
  final ByteBuf buf;
  final int length;
  int offset;
  int endOffset;
  Component(ByteBuf buf) {
    this.buf = buf;
    length = buf.readableBytes();
  }
  void freeIfNecessary() {
    buf.release(); // We should not get a NPE here. If so, it must be a bug.
  }
}

3.2、ByteBufUtil

ByteBufUtil是ByteBuf的一個(gè)靜態(tài)工具類蓄拣,其提供了一系列靜態(tài)方法用于操作ByteBuf對(duì)象扬虚。

操作方法如下:

ByteBufUtil操作方法2.png

操作方法很豐富,主要包括:

  • 返回ByteBuf中可讀字節(jié)的十六進(jìn)制字符串球恤,如hexDump()等辜昵;
  • 字符串編解碼,如encodeString()咽斧,decodeString()等堪置;
  • ByteBuf字符串比較;
  • ByteBuf緩沖區(qū)拷貝等张惹;

相關(guān)閱讀:

Netty源碼愫讀(二)Channel相關(guān)源碼學(xué)習(xí)【http://www.reibang.com/p/02eac974258e
Netty源碼愫讀(三)ChannelPipeline舀锨、ChannelHandlerContext相關(guān)源碼學(xué)習(xí)【http://www.reibang.com/p/be82d0fcdbcc
Netty源碼愫讀(四)ChannelHandler相關(guān)源碼學(xué)習(xí)【http://www.reibang.com/p/6ee0a3b9d73a
Netty源碼愫讀(五)EventLoop與EventLoopGroup相關(guān)源碼學(xué)習(xí)【http://www.reibang.com/p/05096995d296
Netty源碼愫讀(六)ServerBootstrap相關(guān)源碼學(xué)習(xí)【http://www.reibang.com/p/a71a9a0291f3

參考書(shū)籍:
李林鋒《Netty權(quán)威指南》第二版
《Netty 實(shí)戰(zhàn)(精髓)》

參考博客:
https://www.cnblogs.com/wade-luffy/p/6196481.html
https://my.oschina.net/7001/blog/743240
https://my.oschina.net/7001/blog/742236
https://blog.csdn.net/jeffleo/article/details/69230112
http://www.reibang.com/p/c4bd37a3555b

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市宛逗,隨后出現(xiàn)的幾起案子坎匿,更是在濱河造成了極大的恐慌,老刑警劉巖,帶你破解...
    沈念sama閱讀 218,546評(píng)論 6 507
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件碑诉,死亡現(xiàn)場(chǎng)離奇詭異彪腔,居然都是意外死亡,警方通過(guò)查閱死者的電腦和手機(jī)进栽,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,224評(píng)論 3 395
  • 文/潘曉璐 我一進(jìn)店門(mén)德挣,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái),“玉大人快毛,你說(shuō)我怎么就攤上這事格嗅。” “怎么了唠帝?”我有些...
    開(kāi)封第一講書(shū)人閱讀 164,911評(píng)論 0 354
  • 文/不壞的土叔 我叫張陵屯掖,是天一觀的道長(zhǎng)。 經(jīng)常有香客問(wèn)我襟衰,道長(zhǎng)贴铜,這世上最難降的妖魔是什么? 我笑而不...
    開(kāi)封第一講書(shū)人閱讀 58,737評(píng)論 1 294
  • 正文 為了忘掉前任瀑晒,我火速辦了婚禮绍坝,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘苔悦。我一直安慰自己轩褐,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,753評(píng)論 6 392
  • 文/花漫 我一把揭開(kāi)白布玖详。 她就那樣靜靜地躺著把介,像睡著了一般。 火紅的嫁衣襯著肌膚如雪蟋座。 梳的紋絲不亂的頭發(fā)上拗踢,一...
    開(kāi)封第一講書(shū)人閱讀 51,598評(píng)論 1 305
  • 那天,我揣著相機(jī)與錄音向臀,去河邊找鬼巢墅。 笑死,一個(gè)胖子當(dāng)著我的面吹牛飒硅,可吹牛的內(nèi)容都是我干的砂缩。 我是一名探鬼主播,決...
    沈念sama閱讀 40,338評(píng)論 3 418
  • 文/蒼蘭香墨 我猛地睜開(kāi)眼三娩,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼庵芭!你這毒婦竟也來(lái)了?” 一聲冷哼從身側(cè)響起雀监,我...
    開(kāi)封第一講書(shū)人閱讀 39,249評(píng)論 0 276
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤双吆,失蹤者是張志新(化名)和其女友劉穎眨唬,沒(méi)想到半個(gè)月后,有當(dāng)?shù)厝嗽跇?shù)林里發(fā)現(xiàn)了一具尸體好乐,經(jīng)...
    沈念sama閱讀 45,696評(píng)論 1 314
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡匾竿,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,888評(píng)論 3 336
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了蔚万。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片岭妖。...
    茶點(diǎn)故事閱讀 40,013評(píng)論 1 348
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖反璃,靈堂內(nèi)的尸體忽然破棺而出昵慌,到底是詐尸還是另有隱情,我是刑警寧澤淮蜈,帶...
    沈念sama閱讀 35,731評(píng)論 5 346
  • 正文 年R本政府宣布斋攀,位于F島的核電站,受9級(jí)特大地震影響梧田,放射性物質(zhì)發(fā)生泄漏淳蔼。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,348評(píng)論 3 330
  • 文/蒙蒙 一裁眯、第九天 我趴在偏房一處隱蔽的房頂上張望鹉梨。 院中可真熱鬧,春花似錦未状、人聲如沸俯画。這莊子的主人今日做“春日...
    開(kāi)封第一講書(shū)人閱讀 31,929評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)。三九已至泡仗,卻和暖如春埋虹,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背娩怎。 一陣腳步聲響...
    開(kāi)封第一講書(shū)人閱讀 33,048評(píng)論 1 270
  • 我被黑心中介騙來(lái)泰國(guó)打工搔课, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人截亦。 一個(gè)月前我還...
    沈念sama閱讀 48,203評(píng)論 3 370
  • 正文 我出身青樓爬泥,卻偏偏與公主長(zhǎng)得像,于是被迫代替她去往敵國(guó)和親崩瓤。 傳聞我的和親對(duì)象是個(gè)殘疾皇子袍啡,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,960評(píng)論 2 355

推薦閱讀更多精彩內(nèi)容