W3Cschool
恭喜您成為首批注冊用戶
獲得88經(jīng)驗(yàn)值獎(jiǎng)勵(lì)
既然所有的網(wǎng)絡(luò)通信都是要基于底層的字節(jié)流來傳輸,那么傳輸所使用的數(shù)據(jù)接口就要求是效率高得、使用方便的而且容易使用的,Netty的ByteBuf更好能夠達(dá)到這些要求。
ByteBuf 是一個(gè)已經(jīng)經(jīng)過優(yōu)化的很好使用的數(shù)據(jù)容器,字節(jié)數(shù)據(jù)可以有效的被添加到 ByteBuf 中或者也可以從 ByteBuf 中直接獲取數(shù)據(jù)。ByteBuf中有兩個(gè)索引:一個(gè)用來讀,一個(gè)用來寫。這兩個(gè)索引達(dá)到了便于操作的目的。我們可以按順序的讀取數(shù)據(jù),也可以通過調(diào)整讀取數(shù)據(jù)的索引或者直接將讀取位置索引作為參數(shù)傳遞給get方法來重復(fù)讀取數(shù)據(jù)。
寫入數(shù)據(jù)到 ByteBuf 后,writerIndex(寫入索引)增加寫入的字節(jié)數(shù)。讀取字節(jié)后,readerIndex(讀取索引)也增加讀取出的字節(jié)數(shù)。你可以讀取字節(jié),直到寫入索引和讀取索引處在相同的位置。此時(shí)ByteBuf不可讀,所以下一次讀操作將會拋出 IndexOutOfBoundsException,就像讀取數(shù)組時(shí)越位一樣。
調(diào)用 ByteBuf 的以 "read" 或 "write" 開頭的任何方法都將自動增加相應(yīng)的索引。另一方面,"set" 、 "get"操作字節(jié)將不會移動索引位置,它們只會在指定的相對位置上操作字節(jié)。
可以給ByteBuf指定一個(gè)最大容量值,這個(gè)值限制著ByteBuf的容量。任何嘗試將寫入超過這個(gè)值的數(shù)據(jù)的行為都將導(dǎo)致拋出異常。ByteBuf 的默認(rèn)最大容量限制是 Integer.MAX_VALUE。
ByteBuf 類似于一個(gè)字節(jié)數(shù)組,最大的區(qū)別是讀和寫的索引可以用來控制對緩沖區(qū)數(shù)據(jù)的訪問。下圖顯示了一個(gè)容量為16的空的 ByteBuf 的布局和狀態(tài),writerIndex 和 readerIndex 都在索引位置 0 :
最常用的模式是 ByteBuf 將數(shù)據(jù)存儲在 JVM 的堆空間,這是通過將數(shù)據(jù)存儲在數(shù)組的實(shí)現(xiàn)。堆緩沖區(qū)可以快速分配,當(dāng)不使用時(shí)也可以快速釋放。它還提供了直接訪問數(shù)組的方法,通過 ByteBuf.array() 來獲取 byte[]數(shù)據(jù)。 這種方法,正如清單5.1中所示的那樣,是非常適合用來處理遺留數(shù)據(jù)的。
Listing 5.1 Backing array
ByteBuf heapBuf = ...;
if (heapBuf.hasArray()) { //1
byte[] array = heapBuf.array(); //2
int offset = heapBuf.arrayOffset() + heapBuf.readerIndex(); //3
int length = heapBuf.readableBytes();//4
handleArray(array, offset, length); //5
}
1.檢查 ByteBuf 是否有支持?jǐn)?shù)組。
2.如果有的話,得到引用數(shù)組。
3.計(jì)算第一字節(jié)的偏移量。
4.獲取可讀的字節(jié)數(shù)。
5.使用數(shù)組,偏移量和長度作為調(diào)用方法的參數(shù)。
注意:
“直接緩沖區(qū)”是另一個(gè) ByteBuf 模式。對象的所有內(nèi)存分配發(fā)生在 堆,對不對?好吧,并非總是如此。在 JDK1.4 中被引入 NIO 的ByteBuffer 類允許 JVM 通過本地方法調(diào)用分配內(nèi)存,其目的是
這就解釋了為什么“直接緩沖區(qū)”對于那些通過 socket 實(shí)現(xiàn)數(shù)據(jù)傳輸?shù)膽?yīng)用來說,是一種非常理想的方式。如果你的數(shù)據(jù)是存放在堆中分配的緩沖區(qū),那么實(shí)際上,在通過 socket 發(fā)送數(shù)據(jù)之前,JVM 需要將先數(shù)據(jù)復(fù)制到直接緩沖區(qū)。
但是直接緩沖區(qū)的缺點(diǎn)是在內(nèi)存空間的分配和釋放上比堆緩沖區(qū)更復(fù)雜,另外一個(gè)缺點(diǎn)是如果要將數(shù)據(jù)傳遞給遺留代碼處理,因?yàn)閿?shù)據(jù)不是在堆上,你可能不得不作出一個(gè)副本,如下:
Listing 5.2 Direct buffer data access
ByteBuf directBuf = ...
if (!directBuf.hasArray()) { //1
int length = directBuf.readableBytes();//2
byte[] array = new byte[length]; //3
directBuf.getBytes(directBuf.readerIndex(), array); //4
handleArray(array, 0, length); //5
}
1.檢查 ByteBuf 是不是由數(shù)組支持。如果不是,這是一個(gè)直接緩沖區(qū)。
2.獲取可讀的字節(jié)數(shù)
3.分配一個(gè)新的數(shù)組來保存字節(jié)
4.字節(jié)復(fù)制到數(shù)組
5.將數(shù)組,偏移量和長度作為參數(shù)調(diào)用某些處理方法
顯然,這比使用數(shù)組要多做一些工作。因此,如果你事前就知道容器里的數(shù)據(jù)將作為一個(gè)數(shù)組被訪問,你可能更愿意使用堆內(nèi)存。
最后一種模式是復(fù)合緩沖區(qū),我們可以創(chuàng)建多個(gè)不同的 ByteBuf,然后提供一個(gè)這些 ByteBuf 組合的視圖。復(fù)合緩沖區(qū)就像一個(gè)列表,我們可以動態(tài)的添加和刪除其中的 ByteBuf,JDK 的 ByteBuffer 沒有這樣的功能。
Netty 提供了 ByteBuf 的子類 CompositeByteBuf 類來處理復(fù)合緩沖區(qū),CompositeByteBuf 只是一個(gè)視圖。
警告
CompositeByteBuf.hasArray() 總是返回 false,因?yàn)樗赡芗劝丫彌_區(qū),也包含直接緩沖區(qū)
例如,一條消息由 header 和 body 兩部分組成,將 header 和 body 組裝成一條消息發(fā)送出去,可能 body 相同,只是 header 不同,使用CompositeByteBuf 就不用每次都重新分配一個(gè)新的緩沖區(qū)。下圖顯示CompositeByteBuf 組成 header 和 body:
Figure 5.2 CompositeByteBuf holding a header and body
下面代碼顯示了使用 JDK 的 ByteBuffer 的一個(gè)實(shí)現(xiàn)。兩個(gè) ByteBuffer 的數(shù)組創(chuàng)建保存消息的組件,第三個(gè)創(chuàng)建用于保存所有數(shù)據(jù)的副本。
Listing 5.3 Composite buffer pattern using ByteBuffer
// 使用數(shù)組保存消息的各個(gè)部分
ByteBuffer[] message = { header, body };
// 使用副本來合并這兩個(gè)部分
ByteBuffer message2 = ByteBuffer.allocate(
header.remaining() + body.remaining());
message2.put(header);
message2.put(body);
message2.flip();
這種做法顯然是低效的;分配和復(fù)制操作不是最優(yōu)的方法,操縱數(shù)組使代碼顯得很笨拙。
下面看使用 CompositeByteBuf 的改進(jìn)版本
Listing 5.4 Composite buffer pattern using CompositeByteBuf
CompositeByteBuf messageBuf = ...;
ByteBuf headerBuf = ...; // 可以支持或直接
ByteBuf bodyBuf = ...; // 可以支持或直接
messageBuf.addComponents(headerBuf, bodyBuf);
// ....
messageBuf.removeComponent(0); // 移除頭 //2
for (int i = 0; i < messageBuf.numComponents(); i++) { //3
System.out.println(messageBuf.component(i).toString());
}
1.追加 ByteBuf 實(shí)例的 CompositeByteBuf
2.刪除 索引1的 ByteBuf
3.遍歷所有 ByteBuf 實(shí)例。
清單5.4 所示,你可以簡單地把 CompositeByteBuf 當(dāng)作一個(gè)可迭代遍歷的容器。 CompositeByteBuf 不允許訪問其內(nèi)部可能存在的支持?jǐn)?shù)組,也不允許直接訪問數(shù)據(jù),這一點(diǎn)類似于直接緩沖區(qū)模式,如圖5.5所示。
Listing 5.5 Access data
CompositeByteBuf compBuf = ...;
int length = compBuf.readableBytes(); //1
byte[] array = new byte[length]; //2
compBuf.getBytes(compBuf.readerIndex(), array); //3
handleArray(array, 0, length); //4
1.得到的可讀的字節(jié)數(shù)。
2.分配一個(gè)新的數(shù)組,數(shù)組長度為可讀字節(jié)長度。
3.讀取字節(jié)到數(shù)組
4.使用數(shù)組,把偏移量和長度作為參數(shù)
Netty 嘗試使用 CompositeByteBuf 優(yōu)化 socket I/O 操作,消除 原生 JDK 中可能存在的的性能低和內(nèi)存消耗問題。雖然這是在Netty 的核心代碼中進(jìn)行的優(yōu)化,并且是不對外暴露的,但是作為開發(fā)者還是應(yīng)該意識到其影響。
CompositeByteBuf API
CompositeByteBuf 提供了大量的附加功能超出了它所繼承的 ByteBuf。請參閱的 Netty 的 Javadoc 文檔 API。
Copyright©2021 w3cschool編程獅|閩ICP備15016281號-3|閩公網(wǎng)安備35020302033924號
違法和不良信息舉報(bào)電話:173-0602-2364|舉報(bào)郵箱:jubao@eeedong.com
掃描二維碼
下載編程獅App
編程獅公眾號
聯(lián)系方式:
更多建議: