在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如圖:
寫(xiě)入N字節(jié)后的ByteBuf如圖:
讀取M(M<N)字節(jié)后的ByteBuf如圖:
調(diào)用discarReadBytes()之后的ByteBuf如圖:
調(diào)用clear()操作之后的ByteBuf如圖:
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ū)域。
作來(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ī)寫(xiě)操作:
2寺惫、ByteBuf實(shí)現(xiàn)類分析
2.1、ByteBuf類繼承圖
- 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枕赵、讀操作簇
在讀取數(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ě)操作簇
首先校驗(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、操作索引
設(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ū)分配器楣导。
類繼承圖如下:
主要功能列表:
方法名稱 | 返回值 | 功能說(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ì)象扬虚。
操作方法如下:
操作方法很豐富,主要包括:
- 返回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