日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當(dāng)前位置: 首頁 > 编程资源 > 编程问答 >内容正文

编程问答

Netty 系列三(ByteBuf).

發(fā)布時間:2024/4/17 编程问答 37 豆豆
生活随笔 收集整理的這篇文章主要介紹了 Netty 系列三(ByteBuf). 小編覺得挺不錯的,現(xiàn)在分享給大家,幫大家做個參考.

一、概述和原理

? ? 網(wǎng)絡(luò)數(shù)據(jù)傳輸?shù)幕締挝豢偸亲止?jié),Netty 提供了 ByteBuf 作為它的字節(jié)容器,既解決了 JDK API 的局限性,又為網(wǎng)絡(luò)應(yīng)用程序提供了更好的 API,ByteBuf 的優(yōu)點(diǎn):

1、可以被用戶自定義的緩沖區(qū)類型擴(kuò)展
2、通過內(nèi)置的復(fù)合緩沖區(qū)類型實(shí)現(xiàn)了透明的零拷貝
3、容量可以按需增長
4、在讀和寫這兩種模式之間切換不需要調(diào)用 ByteBuffer 的 flip()方法
5、讀和寫使用了不同的索引
6、支持方法的鏈?zhǔn)秸{(diào)用
7、支持引用計數(shù)
8、支持池化

? ??ByteBuf通過兩個索引(readerIndex、writerIndex)劃分為三個區(qū)域:

1、任何名稱以 read 或者 skip 開頭的操作都將檢索或者跳過位于當(dāng)前readerIndex 的數(shù)據(jù),并且將它增加已讀字節(jié)數(shù);任何名稱以 write 開頭的操作都將從當(dāng)前的 writerIndex 處開始寫數(shù)據(jù),并將它增加已經(jīng)寫入的字節(jié)數(shù)。readerIndex 不能超過?writerIndex。

2、如果被調(diào)用的方法需要一個 ByteBuf 參數(shù)作為讀取的目標(biāo),并且沒有指定目標(biāo)索引參數(shù),那么該目標(biāo)緩沖區(qū)的 writerIndex 也將被增加;如果寫操作的目標(biāo)也是 ByteBuf,并且沒有指定源索引的值,則源緩沖區(qū)的 readerIndex 也同樣會被增加相同的大小。

3、如果嘗試在緩沖區(qū)的可讀字節(jié)數(shù)已經(jīng)耗盡時從中讀取數(shù)據(jù), 那么將會引發(fā)一個IndexOutOfBoundsException;如果嘗試往目標(biāo)寫入超過目標(biāo)容量的數(shù)據(jù),將檢查當(dāng)前的寫索引以及最大容量是否可以在擴(kuò)展后容納該數(shù)據(jù),可以的話就會分配并調(diào)整容量,否則將會引發(fā)一個IndexOutOfBoundException。

4、通過調(diào)用 discardReadBytes()方法, 可以丟棄已讀字節(jié)并回收空間。但不建議頻繁的調(diào)用discardReadBytes(),因?yàn)閷⒖赡軐?dǎo)致內(nèi)存復(fù)制:

5、通過調(diào)用 clear()方法來將 readerIndex writerIndex 都設(shè)置為 0。 注意,調(diào)用 clear()比調(diào)用 discardReadBytes()輕量得多, 因?yàn)樗鼘⒅皇侵刂盟饕粫?fù)制任何的內(nèi)存。?

二、ByteBuf 分配

? ??ByteBuf 是一個抽象類,在程序中直接new ByteBuf() 是不現(xiàn)實(shí)的啦!畢竟還要實(shí)現(xiàn)一堆的方法,并且缺乏池化的管理。那么,在 Netty 中要怎么得到 ByteBuf 呢?

? ? 有兩種方法可以得到 ByteBuf 實(shí)例,一種是?ByteBufAllocator (實(shí)現(xiàn)了池化,有效的降低了分配和釋放內(nèi)存的開銷),另一種是?Unpooled (Netty 提供的工具類來創(chuàng)建未池化的ByteBuf 實(shí)例)。
? ??ByteBufAllocator:Netty 提供了兩種?ByteBufAllocator 的實(shí)現(xiàn),PooledByteBufAllocator(默認(rèn)使用)和UnpooledByteBufAllocator。前者池化了ByteBuf的實(shí)例以提高性能并最大限度地減少內(nèi)存碎片;后者的實(shí)現(xiàn)不池化ByteBuf實(shí)例, 并且在每次它被調(diào)用時都會返回一個新的實(shí)例。

? ? 創(chuàng)建 ButeBuf 實(shí)例的代碼如下:

public void createBuf() {//獲得ByteBufAllocator 的兩種方式//1、Channel channel = null;ByteBufAllocator alloc = channel.alloc();//2、ChannelHandlerContext channelHandlerContext = null;ByteBufAllocator alloc1 = channelHandlerContext.alloc();
//ByteBufAllocator 創(chuàng)建 ByteBuf 實(shí)例//1、返回一個基于堆或者直接內(nèi)存存儲的ByteBufByteBuf byteBuf = alloc.buffer(256, Integer.MAX_VALUE);//2、返回一個基于堆內(nèi)存存儲的 ByteBuf 實(shí)例ByteBuf byteBuf1 = alloc.heapBuffer(256);
byteBuf1.refCnt();
//檢查該ByteBuf的引用計數(shù)byteBuf1.release();//將ByteBuf的引用計數(shù)設(shè)為0并釋放
//3、返回一個基于直接內(nèi)存存儲的 ByteBufByteBuf byteBuf2 = alloc.directBuffer();//4、返回一個可以通過添加最大到指定數(shù)目的基于堆的或者直接內(nèi)存存儲的緩沖區(qū)來擴(kuò)展的 CompositeByteBufCompositeByteBuf compositeByteBuf = alloc.compositeBuffer();CompositeByteBuf compositeByteBuf1 = alloc.compositeHeapBuffer(16);CompositeByteBuf compositeByteBuf2 = alloc.compositeDirectBuffer(16);//5、返回一個用于套接字的 I/O 操作的ByteBufByteBuf byteBuf3 = alloc.ioBuffer();

//Unpooled 創(chuàng)建 ByteBuf 實(shí)例//1、創(chuàng)建一個未池化的基于堆內(nèi)存存儲的 ByteBuf 實(shí)例ByteBuf buf = Unpooled.buffer();//2、創(chuàng)建一個未池化的基于內(nèi)存存儲的ByteBufByteBuf buf1 = Unpooled.directBuffer(256, Integer.MAX_VALUE);//3、返回一個包裝了給定數(shù)據(jù)的 ByteBufUnpooled.wrappedBuffer("Hello Netty".getBytes());//4、返回一個復(fù)制了給定數(shù)據(jù)的 ByteBufUnpooled.copiedBuffer("Hello Netty",CharsetUtil.UTF_8); }

三、ByteBuf 操作

? ? 涉及到 ByteBuf 的操作主要是兩個方面,一個是 ByteBuf 的讀/寫,另一個是 ByteBuf 的復(fù)制分片操作。

? ? ByteBuf 的讀/寫操作操作有兩種類別:get() 和 set() 操作,從給定的索引開始,并且保持索引不變,也就是說get() 和 set() 操作并不會改變 readerIndex 和 writerIndex 的值;read()和 write()操作, 從給定的索引開始,并且會根據(jù)已經(jīng)訪問過的字節(jié)數(shù)對索引進(jìn)行調(diào)整,比如 read() 操作 readerIndex 會根據(jù)讀取的數(shù)據(jù)類型(byte 1個字節(jié),short 2個字節(jié),int 4個字節(jié),long 8個字節(jié))增加對應(yīng)的索引數(shù)。

? ? ByteBuf 提供了專門的方式來實(shí)現(xiàn)復(fù)制分片操作:

duplicate();
slice();
slice(int, int);
Unpooled.unmodifiableBuffer(…);
order(ByteOrder);
readSlice(int)。

? ??這些方法都將返回一個新的 ByteBuf 實(shí)例,它具有自己的讀索引、寫索引和標(biāo)記索引。其內(nèi)部存儲和 JDK 的 ByteBuffer 一樣也是共享的。這意味著,如果你修改了它的內(nèi)容,也同時修改了其對應(yīng)ByteBuf的源實(shí)例。

? ??如果需要一個現(xiàn)有緩沖區(qū)的真實(shí)副本,請使用 copy()或者 copy(int, int)方法。不同于派生緩沖區(qū),由這個調(diào)用所返回的 ByteBuf 擁有獨(dú)立的數(shù)據(jù)副本。

? ? 現(xiàn)在我們具體來看看 ByteBuf 的操作:

? ? 1、數(shù)據(jù)遍歷

for (int i = 0; i < byteBuf.capacity(); i++) {byte aByte = byteBuf.getByte(i);System.out.print((char) aByte); }while (byteBuf.isReadable()){System.out.print((char) byteBuf.readByte()); }

? ? 2、寫入數(shù)據(jù)

while (byteBuf.writableBytes() >= 4){byteBuf.writeByte(65); }

? ? 3、索引標(biāo)記管理

ByteBuf buf = byteBuf.readerIndex(0);//將 readerIndex 移動到指定的位置 buf.markReaderIndex();//標(biāo)記當(dāng)前的 readerIndex while (buf.isReadable()){System.out.print((char) buf.readByte()); } buf.resetReaderIndex();//回退到之前標(biāo)記的 readerIndex while (buf.isReadable()){System.out.print((char) buf.readByte()); }int index = byteBuf.indexOf(0, byteBuf.capacity() - 1, (byte) 65);//在某個范圍內(nèi)查找某個字節(jié)的索引

? ? 4、分片操作

ByteBuf slice = byteBuf.slice(0, 15); System.out.print(slice.toString(CharsetUtil.UTF_8)); //更新索引0處的字節(jié) slice.setByte(0, (byte) 'J'); byte aByte = byteBuf.getByte(0); System.out.print("\r\n" + (char)aByte);

? ? 5、復(fù)制操作

ByteBuf copy = buf.copy(0, 15); System.out.println(copy.toString(CharsetUtil.UTF_8)); copy.setByte(0, (byte) 'A'); System.out.println((char) byteBuf.getByte(0));

? ? 6、其他操作

System.out.println("如果至少有一個字節(jié)可讀取:" + byteBuf.isReadable()); System.out.println("如果至少有一個字節(jié)可寫入:" + byteBuf.isWritable()); System.out.println("返回可被讀取的字節(jié)數(shù):" + byteBuf.readableBytes()); System.out.println("返回可被寫入的字節(jié)數(shù):" + byteBuf.writableBytes()); System.out.println("可容納的字節(jié)數(shù):" + byteBuf.capacity() + ",可擴(kuò)展最大的字節(jié)數(shù):" + byteBuf.maxCapacity()); System.out.println("是否由一個字節(jié)數(shù)組支撐:" + byteBuf.hasArray()); System.out.println("由一個字節(jié)數(shù)組支撐則返回該數(shù)組:" + byteBuf.array().length); System.out.println("計算第一個字節(jié)的偏移量:" + byteBuf.arrayOffset()); System.out.println("返回Bytebuf的十六進(jìn)制:" + ByteBufUtil.hexDump(byteBuf.array()));

四、補(bǔ)充

ByteBuf 的使用模式 :

? ? 1、堆緩沖區(qū):最常用的 ByteBuf 模式是將數(shù)據(jù)存儲在 JVM 的堆空間中。 這種模式被稱為支撐數(shù)組(backing array), 它能在沒有使用池化的情況下提供快速的分配和釋放。

? ??2、直接緩沖區(qū):將數(shù)據(jù)駐留在會被垃圾回收的堆之外,直接緩沖區(qū)對于網(wǎng)絡(luò)數(shù)據(jù)傳輸是最理想的選擇,不過,相對于基于堆的緩沖區(qū),它們的分配和釋放都較為昂貴。另外,如果你的數(shù)據(jù)包含在一個在堆上分配的緩沖區(qū)中, 那么事實(shí)上,在通過套接字發(fā)送它之前, JVM將會在內(nèi)部把你的緩沖區(qū)復(fù)制到一個直接緩沖區(qū)中。經(jīng)驗(yàn)表明,Bytebuf的最佳實(shí)踐是在IO通信線程的讀寫緩沖區(qū)使用DirectByteBuf,后端業(yè)務(wù)使用HeapByteBuf。

? ? 3、復(fù)合緩沖區(qū):為多個 ByteBuf 提供一個聚合視圖。 在這里你可以根據(jù)需要添加或者刪除 ByteBuf 實(shí)例。

? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?Netty 通過一個 ByteBuf 子類——CompositeByteBuf——實(shí)現(xiàn)了這個模式, 它提供了一個將多個緩沖區(qū)表示為單個合并緩沖區(qū)的虛擬表示。

? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?使用 CompositeByteBuf 的復(fù)合緩沖區(qū)模式:

public void CompositeBuffer() {CompositeByteBuf messageBuf = Unpooled.compositeBuffer();ByteBuf headBuf = Unpooled.copiedBuffer("Hello,", CharsetUtil.UTF_8);ByteBuf bodyBuf = Unpooled.copiedBuffer("Netty!", CharsetUtil.UTF_8);//將 ByteBuf 實(shí)例追加到 CompositeByteBuf messageBuf.addComponents(headBuf, bodyBuf);Iterator<ByteBuf> it = messageBuf.iterator();//訪問CompositeByteBuf數(shù)據(jù)while(it.hasNext()){ByteBuf buf = it.next();while (buf.isReadable()){System.out.print((char) buf.readByte());}}//使用數(shù)組訪問數(shù)據(jù)if(!messageBuf.hasArray()){int len = messageBuf.readableBytes();byte[] arr = new byte[len];messageBuf.getBytes(0, arr);for (byte b : arr){System.out.print((char)b);}}messageBuf.removeComponent(0); //刪除位于索引位置為 0(第一個組件)的 ByteBuf }

?

?

參考資料:《Netty IN ACTION》

演示源代碼:https://github.com/JMCuixy/NettyDemo/blob/master/src/main/java/org/netty/demo/bytebuf/ByteBufOperation.java

總結(jié)

以上是生活随笔為你收集整理的Netty 系列三(ByteBuf).的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網(wǎng)站內(nèi)容還不錯,歡迎將生活随笔推薦給好友。