Netty學(xué)習(xí)之ByteBuf

Netty學(xué)習(xí)之ByteBuf

前言

在網(wǎng)絡(luò)中傳輸?shù)幕締卧亲止?jié)byte,雖然在NIO中提供了一個(gè)ByteBuffer作為字節(jié)的容器逛腿,不過(guò)由于ByteBuffer比較難使用,所以Netty自己實(shí)現(xiàn)了一個(gè)荒辕,ByteBuf策泣,并且提供了比較靈活的操作方式以及操作工具,本節(jié)我們將詳細(xì)學(xué)習(xí)這一部分的知識(shí)跟束。

ByteBuf

在Netty中莺奸,數(shù)據(jù)通過(guò)ByteBuf以及ByteBufHolder來(lái)進(jìn)行操作,ByteBuf具有眾多優(yōu)秀的特性

  • 易于擴(kuò)展
  • 通過(guò)內(nèi)置的composite buffer類型可以實(shí)現(xiàn)zero-copy
  • 根據(jù)需要擴(kuò)展容量冀宴,類似于JDK中的(StringBuilder)
  • 自動(dòng)在讀寫模式進(jìn)行切換灭贷,相比ByteBuffer需要調(diào)用filp()更加方便
  • 采用讀寫兩個(gè)指針
  • 支持鏈?zhǔn)秸{(diào)用
  • 支持引用計(jì)數(shù)
  • 支持池化技術(shù)

工作原理

ByteBuf可以理解為一個(gè)字節(jié)數(shù)組,并且維護(hù)兩個(gè)不同的指針:讀指針以及寫指針略贮,當(dāng)從ByteBuf中讀取數(shù)據(jù)(read開(kāi)頭的函數(shù))的時(shí)候甚疟,讀指針增加,當(dāng)寫入數(shù)據(jù)(write開(kāi)頭的函數(shù))的時(shí)候逃延,寫指針增加览妖,當(dāng)讀寫指針相同時(shí),表示已經(jīng)沒(méi)有數(shù)據(jù)可以讀取揽祥,繼續(xù)讀取會(huì)拋出IndexOutOfBoundException讽膏,set以及get開(kāi)頭的函數(shù)不會(huì)影響指針,默認(rèn)的最大容量是Integer.MAX_VALUE拄丰,當(dāng)寫入超過(guò)容量時(shí)府树,會(huì)觸發(fā)異常俐末。

ByteBuf類型

基于堆的ByteBuf,將數(shù)據(jù)存儲(chǔ)在JVM的堆內(nèi)存中奄侠,并且其內(nèi)部是一個(gè)字節(jié)數(shù)組卓箫,但沒(méi)有進(jìn)行緩存的時(shí)候,可以快速地申請(qǐng)以及回收內(nèi)存垄潮,比較適合處理常規(guī)數(shù)據(jù)丽柿。

基于直接內(nèi)存的ByteBuf,ByteBuf的內(nèi)存空間是通過(guò)直接內(nèi)存申請(qǐng)的(本地方法調(diào)用分配的內(nèi)存)魂挂,好處在于可以避免在發(fā)生I/O調(diào)用的時(shí)候甫题,將數(shù)據(jù)從堆空間拷貝到直接內(nèi)存中(節(jié)省一次拷貝),比較適合于進(jìn)行網(wǎng)絡(luò)數(shù)據(jù)傳輸涂召,由于數(shù)據(jù)沒(méi)有在堆中坠非,所以操作的時(shí)候需要先拷貝到堆空間中(需要手動(dòng)操作),缺點(diǎn)是空間的申請(qǐng)以及回收比較消耗資源果正,而且不受gc的管理炎码。

上面兩個(gè)可以通過(guò)hasArray()進(jìn)行區(qū)分,基于堆的返回true秋泳,基于直接內(nèi)存的返回false

組合ByteBuf(CompositeByteBuf)潦闲,提供了多個(gè)ByteBuf的聚合視圖,可以往其中添加或者刪除ByteBuf實(shí)例迫皱,可能包含上面兩種類型的ByteBuf歉闰,所以使用的時(shí)候需要注意。

獲取ByteBuf

通過(guò)ByteBufAllocator

Netty通過(guò)ByteBufAllocator接口卓起,來(lái)提供獲取ByteBuf的操作

buffer();
buffer(initCapacity);
buffer(initCapacity, maxCapacity);

heapBuffer();
heapBuffer(initCapacity);
heapBuffer(initCapacity, maxCapacity);

directBuffer();
directBuffer(..);
directBuffer(.., ..);

compositeBuffer();
compositeBuffer(..);
compositeDirectBuffer();
compositeDirectBuffer(..);
compositeHeapBuffer();
compositeHeapBuffer(..);

ioBuffer(); // for i/o in socket

可以從Channel或者ChannelHanderContext中獲取ByteBuffAllocator實(shí)例

public void testAllocator() {
    NioServerSocketChannel channel = new NioServerSocketChannel();
    ByteBufAllocator alloc = channel.alloc();
    ByteBuf byteBuf = alloc.heapBuffer();
    System.out.println(byteBuf.hasArray());

    ChannelHandlerContext ctx = ...;
    ByteBufAllocator allocator = ctx.alloc();
}

同時(shí)和敬,Netty提供了兩種ByteBufAllocator的默認(rèn)實(shí)現(xiàn):PooledByteBufAllocator以及UnpooledByteBufAllocator

// true表示調(diào)用buffer()時(shí),使用直接內(nèi)存戏阅,false表示堆內(nèi)存
ByteBufAllocator allocator = new UnpooledByteBufAllocator(true);
ByteBufAllocator allocator = new PooledByteBufAllocator(true);

true跟false的區(qū)別僅在于buffer()昼弟,如果是調(diào)用heapBuffer(),那還是堆內(nèi)存奕筐,跟true/false無(wú)關(guān)

通過(guò)Unpooled

在有一些情況舱痘,我們沒(méi)有辦法獲取ByteBuffAllocator,則可以通過(guò)Unpooled工具來(lái)創(chuàng)建未緩存的ByteBuf實(shí)例

buffer(); // 基于堆的ByteBuff
buffer(..); // 同上

directBuffer(); // 同上

wrappedBuffer(); // 包裝給定內(nèi)容
copiedBuffer(); // 拷貝給定內(nèi)容

ByteBuf操作

隨機(jī)訪問(wèn)

ByteBuf支持類似于數(shù)組的訪問(wèn)形式离赫,并且其下標(biāo)從0開(kāi)始芭逝,最后一個(gè)Byte為capacity() - 1

ByteBuf buffer = Unpooled.copiedBuffer("hello world".getBytes());
for (int i = 0; i < buffer.capacity(); i++) {
    System.out.print((char)buffer.getByte(i));
}

連續(xù)訪問(wèn)

ByteBuf由三個(gè)部分組成,如下圖所示

+----------+------------+----------------+
|          |            |                |
| 已經(jīng)讀取  |  可以讀取   |   可以寫入     |
|          |            |                |
+----------+------------+----------------+
0------readerIndex---writeIndex-----capacity

其中已經(jīng)讀取的數(shù)據(jù)不可能再被讀取(指的是當(dāng)讀指針已經(jīng)移動(dòng)后笆怠,讀指針之前的數(shù)據(jù))铝耻,讀指針與寫指針之間的數(shù)據(jù)則可以被繼續(xù)讀取,寫指針與容量之間的空間可以繼續(xù)寫入。

當(dāng)調(diào)用ByteBuf#discardReadByytes()后瓢捉,后面的數(shù)據(jù)會(huì)移動(dòng)到前面频丘,使得讀指針歸為0光坝,注意該操作會(huì)比較消耗資源惫搏,一般只在內(nèi)存資源比較緊張的時(shí)候才進(jìn)行該操作钻蹬。

移動(dòng)指針

在ByteBuf中脾猛,存在讀寫指針,所以可以根據(jù)需要移動(dòng)指針页眯,當(dāng)調(diào)用read開(kāi)頭的函數(shù)時(shí)财破,讀指針會(huì)移動(dòng)溢吻,write開(kāi)頭的函數(shù)時(shí)靶壮,write指針會(huì)移動(dòng)怔毛,同時(shí),可以調(diào)用readIndex(int)將讀指針設(shè)置到指定位置腾降,超過(guò)可讀位置會(huì)拋出異常拣度,writeIndex(int)同理。

可以調(diào)用clear()將讀寫指針均設(shè)置為0螃壤,該操作并沒(méi)有清空內(nèi)容抗果,也即數(shù)據(jù)依舊可以讀取出來(lái),該操作比discardReadBytes()消耗更低奸晴,因?yàn)橹皇侵刂昧酥羔槨?/p>

搜索操作

用于查找某個(gè)字節(jié)的下標(biāo)冤馏,可以使用indexOf(),也可以使用復(fù)雜的操作ByteBufProcessor#process(byte value)寄啼,同時(shí)ByteBufProcessor定義了一系列公用操作逮光,如ByteBufProcessor.FIND_CRByteBufProcessor.FIND_LF

衍生操作

衍生操作提供了一些可以從當(dāng)前ByteBuf中獲取ByteBuf的操作辕录,其底層公用一個(gè)ByteBuff睦霎,但是具有自己的讀寫指針

public void testDerived() {
    ByteBuf buffer = Unpooled.copiedBuffer("hello world".getBytes());
    // 影子拷貝,底層其實(shí)是同一個(gè)
    ByteBuf duplicate = buffer.duplicate();
    buffer.setByte(0, 'a');
    // a
    System.out.println((char)duplicate.getByte(0));

    // 影子切片走诞,可以帶參數(shù)
    ByteBuf slice = buffer.slice();
    buffer.setByte(0, 'a');
    // a
    System.out.println((char) slice.getByte(0));

    ByteBuf byteBuf = buffer.readSlice(buffer.readableBytes());
    for (int i = 0; i < byteBuf.capacity(); i++) {
        System.out.print((char) byteBuf.getByte(i));
    }
}

如果是要拷貝數(shù)據(jù),則應(yīng)該使用copy()或者copy(int, int)操作

ByteBufUtils

在Netty中蛤高,同時(shí)還提供了ByteBufUtils工具類來(lái)操作ByteBuf蚣旱,如hexDump()可以用于打印ByteBuf的內(nèi)容,更多關(guān)于ByteBufUtils戴陡,可以參考API即可塞绿。

引用計(jì)數(shù)

Netty為ByteBuf以及ByteBufHolder引入了引用計(jì)數(shù),兩者均實(shí)現(xiàn)了ReferenceCounted接口恤批,可以用于提高性能异吻。

當(dāng)引用計(jì)數(shù)的值大于0的時(shí)候,對(duì)應(yīng)的資源不會(huì)被釋放,當(dāng)計(jì)數(shù)值等于0時(shí)诀浪,資源會(huì)被釋放掉棋返。

通常來(lái)說(shuō),最后一個(gè)使用資源的對(duì)象需要釋放掉該資源雷猪,即調(diào)用其release()方法

總結(jié)

本小節(jié)主要詳細(xì)學(xué)習(xí)了Netty中的數(shù)據(jù)容器睛竣,ByteBuf,在Netty中求摇,所有的數(shù)據(jù)都是存放在ByteBuf中射沟,所以,對(duì)Netty中數(shù)據(jù)的操作与境,其實(shí)就是對(duì)ByteBuf的操作验夯,ByteBuf有三種不同的類型,基于堆的摔刁,基于直接內(nèi)存的挥转,組合類型的,在使用的時(shí)候需要根據(jù)情況選擇合適的容器簸搞,同時(shí)扁位,為了提高性能,Netty中引入了引用計(jì)數(shù)的概念趁俊,所以域仇,當(dāng)資源不需要使用的時(shí)候,需要顯示釋放掉對(duì)應(yīng)的資源寺擂。

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末暇务,一起剝皮案震驚了整個(gè)濱河市,隨后出現(xiàn)的幾起案子怔软,更是在濱河造成了極大的恐慌垦细,老刑警劉巖,帶你破解...
    沈念sama閱讀 218,682評(píng)論 6 507
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件挡逼,死亡現(xiàn)場(chǎng)離奇詭異括改,居然都是意外死亡,警方通過(guò)查閱死者的電腦和手機(jī)家坎,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,277評(píng)論 3 395
  • 文/潘曉璐 我一進(jìn)店門嘱能,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái),“玉大人虱疏,你說(shuō)我怎么就攤上這事惹骂。” “怎么了做瞪?”我有些...
    開(kāi)封第一講書人閱讀 165,083評(píng)論 0 355
  • 文/不壞的土叔 我叫張陵对粪,是天一觀的道長(zhǎng)。 經(jīng)常有香客問(wèn)我,道長(zhǎng)著拭,這世上最難降的妖魔是什么纱扭? 我笑而不...
    開(kāi)封第一講書人閱讀 58,763評(píng)論 1 295
  • 正文 為了忘掉前任,我火速辦了婚禮茫死,結(jié)果婚禮上跪但,老公的妹妹穿的比我還像新娘。我一直安慰自己峦萎,他們只是感情好屡久,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,785評(píng)論 6 392
  • 文/花漫 我一把揭開(kāi)白布。 她就那樣靜靜地躺著爱榔,像睡著了一般被环。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上详幽,一...
    開(kāi)封第一講書人閱讀 51,624評(píng)論 1 305
  • 那天筛欢,我揣著相機(jī)與錄音,去河邊找鬼唇聘。 笑死版姑,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的迟郎。 我是一名探鬼主播剥险,決...
    沈念sama閱讀 40,358評(píng)論 3 418
  • 文/蒼蘭香墨 我猛地睜開(kāi)眼,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼宪肖!你這毒婦竟也來(lái)了表制?” 一聲冷哼從身側(cè)響起,我...
    開(kāi)封第一講書人閱讀 39,261評(píng)論 0 276
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤控乾,失蹤者是張志新(化名)和其女友劉穎么介,沒(méi)想到半個(gè)月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體蜕衡,經(jīng)...
    沈念sama閱讀 45,722評(píng)論 1 315
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡壤短,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,900評(píng)論 3 336
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了慨仿。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片鸽扁。...
    茶點(diǎn)故事閱讀 40,030評(píng)論 1 350
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖镶骗,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情躲雅,我是刑警寧澤鼎姊,帶...
    沈念sama閱讀 35,737評(píng)論 5 346
  • 正文 年R本政府宣布,位于F島的核電站,受9級(jí)特大地震影響相寇,放射性物質(zhì)發(fā)生泄漏慰于。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,360評(píng)論 3 330
  • 文/蒙蒙 一唤衫、第九天 我趴在偏房一處隱蔽的房頂上張望婆赠。 院中可真熱鬧,春花似錦佳励、人聲如沸休里。這莊子的主人今日做“春日...
    開(kāi)封第一講書人閱讀 31,941評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)妙黍。三九已至,卻和暖如春瞧剖,著一層夾襖步出監(jiān)牢的瞬間拭嫁,已是汗流浹背。 一陣腳步聲響...
    開(kāi)封第一講書人閱讀 33,057評(píng)論 1 270
  • 我被黑心中介騙來(lái)泰國(guó)打工抓于, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留做粤,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 48,237評(píng)論 3 371
  • 正文 我出身青樓捉撮,卻偏偏與公主長(zhǎng)得像怕品,于是被迫代替她去往敵國(guó)和親。 傳聞我的和親對(duì)象是個(gè)殘疾皇子呕缭,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,976評(píng)論 2 355

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

  • 翻出了之前臨摹的畫堵泽,還記得當(dāng)時(shí)畫的時(shí)候很帶勁,感覺(jué)勾線也挺有意思恢总,當(dāng)時(shí)還覺(jué)得很滿意迎罗,現(xiàn)在再看一看發(fā)現(xiàn)了很多不足之處...
    世上沒(méi)有秘密閱讀 2,434評(píng)論 4 4
  • 昨天Macron上臺(tái)了,今天二戰(zhàn)紀(jì)念日片仿,法國(guó)放假一天纹安。我繼續(xù)倒時(shí)差,順便把家里收拾了砂豌,洗個(gè)澡厢岂,給身體和頭發(fā)做了護(hù)理...
    voutetoilee閱讀 97評(píng)論 0 0
  • 大學(xué)里,好多人的英語(yǔ)阳距,都在走下坡路塔粒,你堅(jiān)持學(xué)習(xí),就在走上坡路筐摘,總有一天卒茬,你會(huì)跟正常水平匯合船老,甚至達(dá)到更高的水平。
    黑板是吸收知識(shí)的地方閱讀 240評(píng)論 0 0
  • 第一次寫空瓶記錄圃酵,也不知道咋地柳畔,想分享一下,希望可以幫助到愛(ài)美的你們郭赐。 坐標(biāo)歐洲薪韩,皮膚在國(guó)內(nèi)是混合油,但是歐洲太干...
    我是青蛙我怕啥閱讀 415評(píng)論 0 1
  • 一:統(tǒng)計(jì)要從反應(yīng)全面情況的統(tǒng)計(jì)數(shù)字著手捌锭。 1:統(tǒng)計(jì)分析要從分析統(tǒng)計(jì)數(shù)字著手俘陷。 2:統(tǒng)計(jì)分析自始至終都要根據(jù)統(tǒng)計(jì)數(shù)字...
    花上閱讀 1,013評(píng)論 0 3