《跟閃電俠學(xué)Netty》閱讀筆記 - 數(shù)據(jù)載體ByteBuf
Part1《跟閃電俠學(xué)Netty》閱讀筆記 - 數(shù)據(jù)載體 ByteBuf
Part2引言
API設(shè)計(jì)更建議實(shí)戰(zhàn)過程中逐漸了解熟悉掌握,本文記錄基礎(chǔ)設(shè)計(jì)和相關(guān)API,只需要大致了解ByteBuf設(shè)計(jì)思想即可。
Part3思維導(dǎo)圖
https://www.mubucm.com/doc/58ehM7v4PP5

Part4基礎(chǔ)結(jié)構(gòu)
整個(gè)ByteBuf的數(shù)據(jù)結(jié)構(gòu)組成如下,整個(gè)設(shè)計(jì)思想有點(diǎn)類似計(jì)算機(jī)如何實(shí)現(xiàn)從北京到上海,那就是一段足夠長的鐵軌,不斷“拆掉”后面的鐵軌放到前面的鐵軌上,這樣實(shí)現(xiàn)火車一直在鐵軌上跑的錯(cuò)覺。
容量上限:maxCapacity
容量:capacity
數(shù)組:
廢棄字節(jié) ?:被丟棄的字節(jié)數(shù)據(jù)無效
可讀字節(jié)(writerIndex -readerIndex)
可寫字節(jié)(capacity - writerIndex)
讀指針 readerIndex :每讀?。╮ead)一個(gè)字節(jié),readerIndex 自增 1
寫指針 writerIndex ?:每寫入(write)一個(gè)字節(jié),writeIndex 自增 1
剩余可用空間

Part5結(jié)構(gòu)解析
廢棄空間 :被丟棄的字節(jié),數(shù)據(jù)無效
可讀空間 :從ByteBuf讀取出來的數(shù)據(jù)都屬于這部分
可寫空間 :未來所有的寫入都會(huì)寫入到此處
字節(jié)容器:分為三部分
讀指針
寫指針
總?cè)萘?/p>
劃分依據(jù):兩個(gè)指針加一個(gè)變量
容量可以在寫滿的時(shí)候擴(kuò)容
如果擴(kuò)容到最大容量就報(bào)錯(cuò)
最大容量和和容量限制
Part6容量API
實(shí)踐容量API之前,我們先構(gòu)建ByteBuf。
ByteBuf?buffer?=?ByteBufAllocator.DEFAULT.buffer(9,?100);??
print("allocate?ByteBuf(9,100)?=>?{}?\n",?buffer);
//?allocate?ByteBuf(9,100)?=>?PooledUnsafeDirectByteBuf(ridx:?0,?widx:?0,?cap:?9/100)?
1capacity()
表示 ByteBuf 底層占用了多少字節(jié)的內(nèi)存(包括丟棄的字節(jié)、可讀字節(jié)、可寫字節(jié)),不同的底層實(shí)現(xiàn)機(jī)制有不同的計(jì)算方式,后面我們講 ByteBuf 的分類的時(shí)候會(huì)講到。
從案例看出,默認(rèn)創(chuàng)建的方式容量為初始化指定的容量。
print("allocate?ByteBuf(9,100)?capacity?=>?{}?\n",?buffer.capacity());
//?allocate?ByteBuf(9,100)?capacity?=>?9?
2maxCapacity()
表示 ByteBuf 底層最大能夠占用多少字節(jié)的內(nèi)存,當(dāng)向 ByteBuf 中寫數(shù)據(jù)的時(shí)候,如果發(fā)現(xiàn)容量不足,則進(jìn)行擴(kuò)容,直到擴(kuò)容到 maxCapacity,超過這個(gè)數(shù),就拋異常。
從案例可以得知,如果擴(kuò)容到 100 就會(huì)報(bào)錯(cuò)
print("allocate?ByteBuf(9,100)?maxCapacity?=>?{}?\n",?buffer.maxCapacity());??
//?allocate?ByteBuf(9,100)?maxCapacity?=>?100
3readableBytes() 與 isReadable()
readableBytes() 表示 ByteBuf 當(dāng)前可讀的字節(jié)數(shù),它的值等于 writerIndex-readerIndex,如果兩者相等,則不可讀,isReadable() 方法返回 false。
//?readableBytes()?與?isReadable()
print("allocate?ByteBuf(9,100)?isReadable?=>?{}?\n",?buffer.isReadable());??
print("allocate?ByteBuf(9,100)?readableBytes?=>?{}?\n",?buffer.readableBytes());
//????????allocate?ByteBuf(9,100)?isReadable?=>?false??
//????????allocate?ByteBuf(9,100)?readableBytes?=>?0
//?write?方法改變寫指針??
buffer.writeBytes(new?byte[]{1,?2,?3,?4});
//?改變寫指針?writeBytes(new?byte[]{1,2,3,4})?=>?PooledUnsafeDirectByteBuf(ridx:?0,?widx:?4,?cap:?9/100)?
//?寫入數(shù)據(jù)之后,重新執(zhí)行readableBytes()?與?isReadable()
print("allocate?ByteBuf(9,100)?isReadable?=>?{}?\n",?buffer.isReadable());
print("allocate?ByteBuf(9,100)?readableBytes?=>?{}?\n",?buffer.readableBytes());
//allocate?ByteBuf(9,100)?isReadable?=>?true?
//allocate?ByteBuf(9,100)?readableBytes?=>?4?
4writableBytes()、 isWritable() 與 maxWritableBytes()
writableBytes() 表示 ByteBuf 當(dāng)前可寫的字節(jié)數(shù),它的值等于 capacity-writerIndex,如果兩者相等,則表示不可寫,isWritable() 返回 false。
注意這個(gè)時(shí)候,并不代表不能往 ByteBuf 中寫數(shù)據(jù)了, 如果發(fā)現(xiàn)往 ByteBuf 中寫數(shù)據(jù)寫不進(jìn)去的話,Netty 會(huì)自動(dòng)擴(kuò)容 ByteBuf,直到擴(kuò)容到底層的內(nèi)存大小為 maxCapacity。
maxWritableBytes() 就表示可寫的最大字節(jié)數(shù),它的值等于 maxCapacity-writerIndex。
在初始化構(gòu)建過程中,由于沒有讀寫任何數(shù)據(jù),可以看到他們的值基本和前面計(jì)算的容量是一致的。
ByteBuf?buffer?=?ByteBufAllocator.DEFAULT.buffer(9,?100);?
//?writableBytes()、?isWritable()?與?maxWritableBytes()
print("allocate?ByteBuf(9,100)?writableBytes?=>?{}?\n",?buffer.writableBytes());
print("allocate?ByteBuf(9,100)?isWritable?=>?{}?\n",?buffer.isWritable());
print("allocate?ByteBuf(9,100)?maxWritableBytes?=>?{}?\n",?buffer.maxWritableBytes());
//????????allocate?ByteBuf(9,100)?writableBytes?=>?9
//????????allocate?ByteBuf(9,100)?isWritable?=>?true
//????????allocate?ByteBuf(9,100)?maxWritableBytes?=>?100
Part7讀寫指針相關(guān)的 API
實(shí)踐讀寫指針相關(guān)的 API之前,我們先構(gòu)建初始化ByteBuf。
ByteBuf?buffer?=?ByteBufAllocator.DEFAULT.buffer(9,?100);??
print("allocate?ByteBuf(9,100)?=>?{}?\n",?buffer);??

5readerIndex() 與 readerIndex(int)
readerIndex():返回當(dāng)前的讀指針 readerIndex
readerIndex(int):表示設(shè)置讀指針
在沒有寫入任何數(shù)據(jù)的時(shí)候,讀指針為0。
//?readerIndex()?返回當(dāng)前讀指針??
print("allocate?ByteBuf(9,100)?readerIndex?=>?{}?\n",?buffer.readerIndex());??
//?allocate?ByteBuf(9,100)?readerIndex?=>?0
下面的案例說明讀指針不能越過寫指針的界限。
//?嘗試手動(dòng)重定向?yàn)^指針位置??
//?print("allocate?ByteBuf(9,100)?readerIndex(int)?=>?{}?\n",?buffer.readerIndex(2));??
//?readerIndex:?2,?writerIndex:?0?(expected:?0?<=?readerIndex?<=?writerIndex?<=?capacity(9))
我們寫入一些數(shù)據(jù)之后,再進(jìn)行讀指針重定向。
buffer.writeBytes(new?byte[]{1,?2,?3,?4});
//?重定向讀指針??
print("allocate?ByteBuf(9,100)?readerIndex(int)?=>?{}?\n",?buffer.readerIndex(2));??
print("重定向讀指針?之后?(new?byte[]{1,2,3,4})?=>?{}?\n",?buffer);??
//?allocate?ByteBuf(9,100)?readerIndex(int)?=>?PooledUnsafeDirectByteBuf(ridx:?2,?widx:?4,?cap:?9/100)?
//?重定向讀指針?之后?(new?byte[]{1,2,3,4})?=>?PooledUnsafeDirectByteBuf(ridx:?2,?widx:?4,?cap:?9/100)
6writerIndex() 與 writerIndex(int)
writeIndex()
表示返回當(dāng)前的寫指針 writerIndex。writeIndex(int)
表示設(shè)置寫指針。
案例以初始化寫入四個(gè)字節(jié)之后作為開始。
//?writeIndex()?與?writeIndex(int)
print("allocate?ByteBuf(9,100)?writerIndex?=>?{}?\n",?buffer.writerIndex());
print("allocate?ByteBuf(9,100)?writerIndex(int)?=>?{}?\n",?buffer.writerIndex(2));
//????????allocate?ByteBuf(9,100)?writerIndex?=>?4
//????????allocate?ByteBuf(9,100)?writerIndex(int)?=>?PooledUnsafeDirectByteBuf(ridx:?0,?widx:?2,?cap:?9/100)
7markReaderIndex() 與 resetReaderIndex()
區(qū)別:
markReaderIndex()
:表示把當(dāng)前的讀指針保存起來,resetReaderIndex()
:表示把當(dāng)前的讀指針恢復(fù)到之前保存的值。
下面兩段代碼是等價(jià)的。
//?代碼片段1
int?readerIndex?=?buffer.readerIndex();
//?…?其他操作
buffer.readerIndex(readerIndex);//?代碼片段二
//?(不需要自己定義變量,推薦使用)
buffer.markReaderIndex();
//?…?其他操作
//?resetReaderIndex()?可以恢復(fù)到之前狀態(tài)
//?(解析自定義協(xié)議的數(shù)據(jù)包常用)
buffer.resetReaderIndex();?
希望大家多多使用代碼片段二這種方式,不需要自己定義變量,無論 buffer 當(dāng)作參數(shù)傳遞到哪里,調(diào)用 resetReaderIndex() 都可以恢復(fù)到之前的狀態(tài),在解析自定義協(xié)議的數(shù)據(jù)包的時(shí)候非常常見,推薦大家使用這一對(duì) API markWriterIndex() 與 resetWriterIndex() 這一對(duì) API 的作用與上述一對(duì) API 類似
Part8讀寫API
實(shí)踐讀寫API之前,我們先構(gòu)建ByteBuf。
ByteBuf?buffer?=?ByteBufAllocator.DEFAULT.buffer(9,?100);??
print("allocate?ByteBuf(9,100)?=>?{}?\n",?buffer);??
上面的代碼
8writeBytes(byte[] src) 與 buffer.readBytes(byte[] dst)
writeBytes() 表示把字節(jié)數(shù)組 src 里面的數(shù)據(jù)全部寫到 ByteBuf。注意此方法執(zhí)行之后,會(huì)移動(dòng)前面介紹的 writeIndex 寫指針。
//?write?方法改變寫指針??
buffer.writeBytes(new?byte[]{1,?2,?3,?4});??
print("改變寫指針?writeBytes(new?byte[]{1,2,3,4})?=>?{}?\n",?buffer);??
//?改變寫指針?writeBytes(new?byte[]{1,2,3,4})?=>?PooledUnsafeDirectByteBuf(ridx:?0,?widx:?4,?cap:?9/100)
readBytes()
指的是把 ByteBuf 里面的數(shù)據(jù)全部讀取到 dst。
//只有read方法改變指針??
byte[]?bytes?=?new?byte[buffer.readableBytes()];??
buffer.readBytes(bytes);??
??
print("bytes?內(nèi)容?=>?{}",?bytes);??
//?bytes?內(nèi)容?=>??????
這里 dst 字節(jié)數(shù)組的大小通常等于 readableBytes(),而 src 字節(jié)數(shù)組大小的長度通常小于等于writableBytes()。
9writeByte(byte b) 與 buffer.readByte()
writeByte() 表示往 ByteBuf 中寫一個(gè)字節(jié),而 buffer.readByte() 表示從 ByteBuf 中讀取一個(gè)字節(jié),類似的 API 還有 writeBoolean()、writeChar()、writeShort()、writeInt()、writeLong()、writeFloat()、writeDouble() 與 readBoolean()、readChar()、readShort()、readInt()、readLong()、readFloat()、readDouble()
這里就不一一贅述。
//?write?方法改變寫指針??
buffer.writeBytes(new?byte[]{1,?2,?3,?4});??
print("改變寫指針?writeBytes(new?byte[]{1,2,3,4})?=>?{}?\n",?buffer);??
//?改變寫指針?writeBytes(new?byte[]{1,2,3,4})?=>?PooledUnsafeDirectByteBuf(ridx:?0,?widx:?4,?cap:?9/100)buffer.writeByte(5);??
print("改變寫指針?buffer.writeByte(5)?=>?{}?\n",?buffer);??
//?改變寫指針?buffer.writeByte(5)?=>?PooledUnsafeDirectByteBuf(ridx:?0,?widx:?5,?cap:?9/100)
getBytes、getByte() 與 setBytes()、setByte() 系列,唯一的區(qū)別就是 get/set 不會(huì)改變讀寫指針,而 read/write 會(huì)改變讀寫指針。
//只有read方法改變指針??
byte[]?bytes?=?new?byte[buffer.readableBytes()];??
buffer.readBytes(bytes);
print("buffer.readBytes(bytes)?=>?{}\n",?buffer);??
//?buffer.readBytes(bytes)?=>?PooledUnsafeDirectByteBuf(ridx:?10,?widx:?10,?cap:?16/100)
10release() 與 retain()
由于 Netty 使用了堆外內(nèi)存,而堆外內(nèi)存是不被 jvm 直接管理的,也就是說申請(qǐng)到的內(nèi)存無法被垃圾回收器直接回收,所以需要我們手動(dòng)回收。
Netty 的 ByteBuf 是通過引用計(jì)數(shù)的方式管理的,如果一個(gè) ByteBuf 沒有地方被引用到,就需要回收底層內(nèi)存。
默認(rèn)情況下,當(dāng)創(chuàng)建完一個(gè) ByteBuf,它的引用為1,然后每次調(diào)用 retain() 方法, 它的引用就加一, release() 方法原理是將引用計(jì)數(shù)減一,減完之后如果發(fā)現(xiàn)引用計(jì)數(shù)為0,則直接回收 ByteBuf 底層的內(nèi)存。
Part9Test
最后是簡單測(cè)試程序整合前面的API案例。
public?class?ByteBufTest?{
????public?static?void?main(String[]?args)?{
????????ByteBuf?buffer?=?ByteBufAllocator.DEFAULT.buffer(9,?100);
????????print("allocate?ByteBuf(9,?100)",?buffer);
????????//?write?方法改變寫指針,寫完之后寫指針未到?capacity?的時(shí)候,buffer?仍然可寫
????????buffer.writeBytes(new?byte[]{1,?2,?3,?4});
????????print("writeBytes(1,2,3,4)",?buffer);
????????//?write?方法改變寫指針,寫完之后寫指針未到?capacity?的時(shí)候,buffer?仍然可寫,?寫完?int?類型之后,寫指針增加4
????????buffer.writeInt(12);
????????print("writeInt(12)",?buffer);
????????//?write?方法改變寫指針,?寫完之后寫指針等于?capacity?的時(shí)候,buffer?不可寫
????????buffer.writeBytes(new?byte[]{5});
????????print("writeBytes(5)",?buffer);
????????//?write?方法改變寫指針,寫的時(shí)候發(fā)現(xiàn)?buffer?不可寫則開始擴(kuò)容,擴(kuò)容之后?capacity?隨即改變
????????buffer.writeBytes(new?byte[]{6});
????????print("writeBytes(6)",?buffer);
????????//?get?方法不改變讀寫指針
????????System.out.println("getByte(3)?return:?"?+?buffer.getByte(3));
????????System.out.println("getShort(3)?return:?"?+?buffer.getShort(3));
????????System.out.println("getInt(3)?return:?"?+?buffer.getInt(3));
????????print("getByte()",?buffer);
????????//?set?方法不改變讀寫指針
????????buffer.setByte(buffer.readableBytes()?+?1,?0);
????????print("setByte()",?buffer);
????????//?read?方法改變讀指針
????????byte[]?dst?=?new?byte[buffer.readableBytes()];
????????buffer.readBytes(dst);
????????print("readBytes("?+?dst.length?+?")",?buffer);
????}
????private?static?void?print(String?action,?ByteBuf?buffer)?{
????????System.out.println("after?==========="?+?action?+?"============");
????????System.out.println("capacity():?"?+?buffer.capacity());
????????System.out.println("maxCapacity():?"?+?buffer.maxCapacity());
????????System.out.println("readerIndex():?"?+?buffer.readerIndex());
????????System.out.println("readableBytes():?"?+?buffer.readableBytes());
????????System.out.println("isReadable():?"?+?buffer.isReadable());
????????System.out.println("writerIndex():?"?+?buffer.writerIndex());
????????System.out.println("writableBytes():?"?+?buffer.writableBytes());
????????System.out.println("isWritable():?"?+?buffer.isWritable());
????????System.out.println("maxWritableBytes():?"?+?buffer.maxWritableBytes());
????????System.out.println();
????}
}
最后是個(gè)人的實(shí)驗(yàn)Test類。
??
/**??
?*?byteBuf?的API測(cè)試??
?*/??
public?class?ByteBufTest?{??
??
????public?static?void?main(String[]?args)?{??
????????//?9?代表初始容量,?100代表最大容量??
????????ByteBuf?buffer?=?ByteBufAllocator.DEFAULT.buffer(9,?100);??
????????print("allocate?ByteBuf(9,100)?=>?{}?\n",?buffer);??
????????//allocate?ByteBuf(9,100)?=>?PooledUnsafeDirectByteBuf(ridx:?0,?widx:?0,?cap:?9/100)??
??
????????//?write?方法改變寫指針??
????????buffer.writeBytes(new?byte[]{1,?2,?3,?4});??
????????print("改變寫指針?writeBytes(new?byte[]{1,2,3,4})?=>?{}?\n",?buffer);??
????????//?改變寫指針?writeBytes(new?byte[]{1,2,3,4})?=>?PooledUnsafeDirectByteBuf(ridx:?0,?widx:?4,?cap:?9/100)??
????????//?write?改變寫指針,如果沒有到達(dá)?capacity?依然可以寫入,寫入?int?之后寫指針增加4??
????????buffer.writeInt(12);??
????????print("buffer.writeInt(12)?=>?{}\n",?buffer);??
????????//?buffer.writeInt(12)?=>?PooledUnsafeDirectByteBuf(ridx:?0,?widx:?8,?cap:?9/100)??
??
????????//?繼續(xù)改變寫指針,當(dāng)前寫入等于??initialCapacity?這個(gè)初始值之后將不能繼續(xù)寫入??
????????buffer.writeBytes(new?byte[]{5});??
????????print("writeBytes(new?byte[]{5})?=>?{}\n",?buffer);??
????????//?writeBytes(new?byte[]{5})?=>?PooledUnsafeDirectByteBuf(ridx:?0,?widx:?9,?cap:?9/100)??
??
????????//?繼續(xù)寫入指針,此時(shí)發(fā)現(xiàn)?已經(jīng)超過?initialCapacity?的值,此時(shí)會(huì)進(jìn)行擴(kuò)容??
????????buffer.writeBytes(new?byte[]{6});??
????????print("writeBytes(new?byte[]{6})?=>?{}\n",?buffer);??
????????//?writeBytes(new?byte[]{6})?=>?PooledUnsafeDirectByteBuf(ridx:?0,?widx:?10,?cap:?16/100)??
??
????????//?get?方法調(diào)用之后不改變讀指針??
????????print("getByte(3)?return?=>?{}\n",?buffer.getByte(3));??
????????print("getShort(3)?return?=>?{}\n",?buffer.getShort(3));??
????????print("getInt(3)?return?=>?{}\n",?buffer.getInt(3));??
????????print("getChar(3)?return?=>?{}\n",?buffer.getChar(3));??
????????/*??
???????getByte(3)?return?=>?4????????getShort(3)?return?=>?1024????????getInt(3)?return?=>?67108864????????getChar(3)?return?=>????
????????*?*/??
????????//?set?方法不改變讀寫指針??
????????buffer.setByte(buffer.readableBytes()?+?1,?0);??
????????print("setByte(buffer.readableBytes()?+?1,?0)?=>?{}\n",?buffer);??
????????//?setByte(buffer.readableBytes()?+?1,?0)?=>?PooledUnsafeDirectByteBuf(ridx:?0,?widx:?10,?cap:?16/100)??
??
????????//只有read方法改變指針??
????????byte[]?bytes?=?new?byte[buffer.readableBytes()];??
????????buffer.readBytes(bytes);??
??
????????print("buffer.readBytes(bytes)?=>?{}\n",?buffer);??
????????//?buffer.readBytes(bytes)?=>?PooledUnsafeDirectByteBuf(ridx:?10,?widx:?10,?cap:?16/100)??
????????print("buffer.readBytes(readBuffer);?=>?{}\n",?buffer);??
??
????????ByteBuf?readBuffer?=?ByteBufAllocator.DEFAULT.buffer(6,?6);??
????????//?原始writeIndex要有足夠空間可讀??
//????????buffer.writeBytes(new?byte[]{5,1,1,1,1,1,1,1});??
??
//????????buffer.writeBytes(new?byte[]{5});??
????????//??readerIndex(10)?+?length(6)?exceeds?writerIndex(11):?PooledUnsafeDirectByteBuf(ridx:?10,?widx:?11,?cap:?16/100)????????buffer.readBytes(readBuffer);??
??
????????System.err.println(readBuffer.readableBytes());??
??
//????????buffer.readBytes(readBuffer);??
????????//?readerIndex(10)?+?length(9)?exceeds?writerIndex(10):?PooledUnsafeDirectByteBuf(ridx:?10,?widx:?10,?cap:?16/100)??
????}??
??
??
}
Part10寫在最后
比較簡單的一個(gè)章節(jié),主要介紹ByteBuf在Java中的使用,整個(gè)使用過程簡單易懂十分清晰。