Netty快速入門(08)ByteBuf組件介紹

前面的內(nèi)容對netty進(jìn)行了介紹病蛉,寫了一個入門例子店茶。作為一個netty的使用者歧焦,我們關(guān)注更多的還是業(yè)務(wù)代碼缺脉。也就是netty中這兩種組件:

ChannelHandler和ChannelPipeline---對應(yīng)于NIO中的客戶邏輯實(shí)現(xiàn)handleRead/handleWrite(interceptor pattern)

ByteBuf---- 對應(yīng)于NIO 中的ByteBuffer

我們的業(yè)務(wù)邏輯要放在handler里面,讀寫數(shù)據(jù)用的是ByteBuf劳秋。其余的Transport仓手、ServerBootstrap、Channel和EventLoop等等都是套路代碼玻淑,對于應(yīng)用程序來說嗽冒,了解即可,基本上不用管补履。真正開發(fā)過netty項(xiàng)目也知道辛慰,項(xiàng)目中大部分都是handler類,其它組件只是占很少一部分干像。




Transport組件

netty做的比較有適應(yīng)性的就是帅腌,不僅支持NIO驰弄,還支持很多傳輸協(xié)議:

OIO -阻塞IO(真正開發(fā)阻塞IO項(xiàng)目,其實(shí)也沒必要用netty了速客。戚篙。。)

NIO -Java NIO

Epoll -? Linux Epoll(JNI)

Local Transport - IntraVM調(diào)用(通訊的雙方在同一個虛擬機(jī)之內(nèi)不再走socket)

Embedded Transport - 供測試使用的嵌入傳輸

UDS-? Unix套接字的本地傳輸(客戶端和服務(wù)端都在同一個服務(wù)器上就可以使用溺职,效率高)

使用不同個傳輸協(xié)議岔擂,只需要在通道里面設(shè)置不同的類型即可:

netty中的channel類型如下:

所以netty設(shè)置和改變傳輸協(xié)議都是一件很簡單的事情。





EventLoopGroup和EventLoop組件

每個EventLoopGroup由多個EventLoop組成浪耘,并且多個EventLoop之間沒有交互乱灵,各做各的事。

每個EventLoop對應(yīng)一個線程(頂層繼承Executor線程池七冲,但是只有一個線程)

所有連接(channel)都將注冊到一個EventLoop痛倚,并且只注冊到一個,整個生命周期中都不會變化

每個EventLoop管理著多個連接(channel)

連接(Channel)上的讀寫事件是由EventLoop來處理的

服務(wù)端創(chuàng)建ServerBootstrap組件的時候澜躺,需要配置兩個EventLoopGroup蝉稳,parentGroup(也就是boss)負(fù)責(zé)處理Accept事件,接收請求掘鄙,childGroup(也就是worker)負(fù)責(zé)處理讀寫事件耘戚。客戶端的Bootstrap組件只需要一個EventLoopGroup即可操漠。





ByteBuf組件

前面討論NIO的時候收津,專門介紹過NIO的Buffer,相對于NIO浊伙,netty的ByteBuf更加易于使用:

為讀/寫分別維護(hù)單獨(dú)的指針朋截,不需要通過flip()進(jìn)行讀/寫模式切換

容量自動伸縮(類似于ArrayList,StringBuilder)

Fluent API (鏈?zhǔn)秸{(diào)用)(ServerBootstrap 組件的配置方式就是鏈?zhǔn)秸{(diào)用)

除了使用上之外吧黄,netty的ByteBuf還擁有更好的性能:

通過內(nèi)置的CompositeBuffer來減少數(shù)據(jù)拷貝(Zero copy)

支持內(nèi)存池,減少GC壓力





ByteBuf組件的操作

ByteBuf通過兩個索引(reader index唆姐、writer index)劃分為三個區(qū)域:

reader index前面的數(shù)據(jù)是已經(jīng)讀過的數(shù)據(jù)拗慨,這些數(shù)據(jù)可以丟棄

從reader index開始,到writer index之前的數(shù)據(jù)是可讀數(shù)據(jù)

從writer index開始奉芦,為可寫區(qū)域

來看下ByteBuf的主要操作赵抢,第一種就是順序的讀寫(改變reader/writer index):

writeByte() - 寫一個字節(jié)

writeLong() - 寫八個字節(jié)

writeXXX() - 所有write方法會讓write index 往前走

readByte() - 讀一個字節(jié)

readLong() - 讀八個字節(jié)

readXXX() - 所有read方法會讓read index往前走

第二種就是隨機(jī)讀寫(不改變read/write index):

getXXX(index)

setXXX(index, byte)

前面NIO中的Buffer操作用,有mark和reset方法声功,用來標(biāo)記操作的狀態(tài)烦却,恢復(fù)狀態(tài),netty中也有類似的方法:

markReaderIndex()

markWriterIndex()

resetReaderIndex()

resetWriterIndex()

writerIndex(index) - 把write index 放置到參數(shù)中的index上面

readerIndex(index) - 把reader index放置到參數(shù)中的index上面

reader index前面的部分是已經(jīng)讀過的是不是浪費(fèi)掉了先巴?netty提供了discardReadBytes方法其爵,把reader index前面的內(nèi)容丟棄掉冒冬,就是把reader index后面的數(shù)據(jù)往前拷貝,這樣空間就可以再利用了摩渺。這個方法和前面NIO中的compact方法類似简烤。還有一個方法就是clear方法,把所有數(shù)據(jù)都清零摇幻,讀索引和寫索引歸零:

netty的ByteBuf中還提供了查詢方法:

indexOf

bytesBefore

forEachByte(ByteBufProcessor)

查詢方法有很多應(yīng)用横侦,比如在信息中有某個字符的存在,就需要做一些操作绰姻,比如每碰到一個換行枉侧,就發(fā)送一條信息,這種功能在聊天中用的很多狂芋。

netty中還有一種衍生緩沖區(qū)榨馁,就是Derived Buffers,可以理解為類似數(shù)據(jù)庫的視圖银酗,是從ByteBuf中衍生出來的辆影,衍生緩沖區(qū)與ByteBuf共享底層的存儲空間,但是它們兩個各自具有各自的index和mark黍特,衍生緩沖區(qū)(Derived Buffers)主要的方法:

duplicate()

slice()

slice(start, stop)

nmodifiableBuffer(...),

衍生緩沖區(qū)(Derived Buffers)是一種淺拷貝蛙讥,如果要進(jìn)行深拷貝怎么用?使用copy或者 copy(int, int) 方法灭衷,會返回有獨(dú)立數(shù)據(jù)副本的ByteBuf次慢。




ByteBuf組件的類型

根據(jù)內(nèi)存的位置,ByteBuf的類型可以分為HeapByteBuf和DirectByteBuf翔曲,這和NIO中的Buffer是一樣的迫像,HeapByteBuf位置在堆上,底層基于數(shù)組-內(nèi)部為一個字節(jié)數(shù)組(byte array)瞳遍,調(diào)用hasArray()方法會返回True闻妓,調(diào)用array()方法返回其內(nèi)部的數(shù)組,可以對數(shù)組進(jìn)行直接操作掠械。DirectByteBuf的位置在堆外內(nèi)存由缆,可以減少拷貝,具有更好的性能猾蒂,但是創(chuàng)建和釋放的開銷更大均唉。Java寫網(wǎng)絡(luò)程序經(jīng)常分成兩個部分,第一個是IO部分肚菠,讀數(shù)據(jù)解碼等舔箭,這些部分用DirectByteBuf效率比較高,因?yàn)檫@部分涉及到向網(wǎng)絡(luò)發(fā)送數(shù)據(jù)需要拷貝蚊逢。如果是其它業(yè)務(wù)相關(guān)的部分层扶,可以使用HeapByteBuf箫章。

根據(jù)是否使用內(nèi)存池,ByteBuf的類型可以分為Pooled和Unpooled兩種ByteBuf怒医,Unpooled就是不用池炉抒,每次都去創(chuàng)建,Pooled類型就是會申請一塊內(nèi)存池稚叹,每次分配都從池中分配焰薄,每次釋放都放回池中。這種主要是針對DirectByteBuf創(chuàng)建和釋放開銷大來制定的策略扒袖,提供一個內(nèi)存池可以提高效率塞茅,減少內(nèi)存碎片,減少GC壓力季率,這種在NIO的Buffer中是沒有的野瘦。

根據(jù)是否使用Unsafe操作,ByteBuf的類型可以分為Safe和Unsafe兩種飒泻,我們知道JDK中有個Unsafe類鞭光,很多JDK中并發(fā)操作的源碼中都用到了這個Unsafe類。直接new可以創(chuàng)建一個safe的ByteBuf泞遗,如果創(chuàng)建Unsafe類型可以直接用Unsafe類操作惰许,效率上有一點(diǎn)點(diǎn)提升,不過這些都是底層操作大家了解即可史辙,而且也不是所有平臺都支持Unsafe操作汹买。

ByteBuf還有一種復(fù)合緩沖區(qū)(CompositeByteBuf),它是由多個ByteBuf組合成的視圖聊倔,是一個ByteBuf列表晦毙,可動態(tài)的添加和刪除其中的ByteBuf。在其中可能既包含堆緩沖區(qū)耙蔑,也包含直接緩沖區(qū)见妒。

netty把這種結(jié)構(gòu)也解釋為一種零拷貝,雖然不是嚴(yán)格意義上的零拷貝甸陌,但是確實(shí)可以提高效率须揣。

來看另外一個接口ByteBufHolder,里面包含了一個ByteBuf邀层,除此之外,還另外存儲一些元數(shù)據(jù)的屬性值遂庄。當(dāng)要拿到里面包含的ByteBuf的時候寥院,就可以拿到這些數(shù)據(jù)。

比如定義一個數(shù)據(jù)包的時候涛目,就可以實(shí)現(xiàn)這個接口秸谢,真正的內(nèi)容放在ByteBuf里面凛澎。






ByteBuf組件的創(chuàng)建

創(chuàng)建的時候,并不需要執(zhí)行new操作估蹄,而是通過ByteBufAllocator分配器來創(chuàng)建塑煎,分配器有兩個實(shí)現(xiàn),分別是UnpooledByteBufAllocator和PooledByteBufAllocator臭蚁,從名字可以看出最铁,分配方式就是是否使用內(nèi)存池的區(qū)別。

主要的方法如下:

為了簡化非池化創(chuàng)建垮兑,netty提供了 Unpooled 的工具類冷尉,它提供了靜態(tài)的輔助方法來創(chuàng)建未池化的ByteBuf實(shí)例,內(nèi)部也包含了UnpooledByteBufAllocator的使用系枪,我們創(chuàng)建非池化的ByteBuf直接用工具類即可雀哨,主要方法如下:

我們前面介紹netty入門例子的時候,服務(wù)端的讀取操作完畢的方法中也用到了這個工具類創(chuàng)建一個空ByteBuf:

而且在客戶端也用到了私爷,

Unpooled.copiedBuffer方法就是說創(chuàng)建一個ByteBuf雾棺,然后把創(chuàng)建的字符串拷貝到ByteBuf中去。





ByteBuf組件隨機(jī)讀寫的示例程序

上面介紹了很多特性和操作衬浑,下面看一個示例程序捌浩,隨機(jī)讀寫,不改變讀寫指針的例子:

上面的每行代碼都有注釋嚎卫,我們看一下結(jié)果:

確實(shí)指針沒有改變嘉栓。這里我們注意獲取讀寫指針用的方法,和隨機(jī)讀寫操作用的方法拓诸,以及如何創(chuàng)建的ByteBuf侵佃。





ByteBuf組件順序讀寫的示例程序

我們再來看一個順序讀寫的例子:

來看打印結(jié)果:

這里除了創(chuàng)建ByteBuf的方式要注意,還要注意順序讀寫的方法奠支。





ByteBuf組件順序讀寫Int數(shù)據(jù)的示例程序

來看一個順序讀寫int類型數(shù)據(jù)的示例:

總長度為20寫入int數(shù)據(jù)只能寫5個馋辈,我們看打印讀取的內(nèi)容:

除了int數(shù)據(jù),其它類型大家也可以試試倍谜。





ByteBuf組件獲取對應(yīng)字符位置的示例程序

ByteProcessor類中定義了很多特殊字符迈螟,有興趣可以看看。來看一下打印效果:





ByteBuf組件slice操作的示例程序

我們看一下打印結(jié)果:

注意slice方法兩個參數(shù)的意義和如何把ByteBuf轉(zhuǎn)換為字符串尔崔。






ByteBuf組件copy深拷貝的示例程序

注意copy方法的用法答毫,來看打印結(jié)果:





ByteBuf組件復(fù)合緩沖區(qū)的示例程序

注意復(fù)合緩沖區(qū)的創(chuàng)建和操作,來看打印結(jié)果:






ByteBuf組件堆上創(chuàng)建和操作的示例程序

來看一下循環(huán)打印的代碼:

連看一下結(jié)果:






ByteBuf組件堆外創(chuàng)建和操作的示例程序

邏輯上和堆上的方法一樣季春,來看一下打印結(jié)果:




代碼地址:https://gitee.com/blueses/netty-demo??06

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末洗搂,一起剝皮案震驚了整個濱河市,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌耘拇,老刑警劉巖撵颊,帶你破解...
    沈念sama閱讀 216,496評論 6 501
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異惫叛,居然都是意外死亡倡勇,警方通過查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,407評論 3 392
  • 文/潘曉璐 我一進(jìn)店門嘉涌,熙熙樓的掌柜王于貴愁眉苦臉地迎上來妻熊,“玉大人,你說我怎么就攤上這事洛心」淘牛” “怎么了?”我有些...
    開封第一講書人閱讀 162,632評論 0 353
  • 文/不壞的土叔 我叫張陵词身,是天一觀的道長厅目。 經(jīng)常有香客問我,道長法严,這世上最難降的妖魔是什么损敷? 我笑而不...
    開封第一講書人閱讀 58,180評論 1 292
  • 正文 為了忘掉前任,我火速辦了婚禮深啤,結(jié)果婚禮上拗馒,老公的妹妹穿的比我還像新娘。我一直安慰自己溯街,他們只是感情好诱桂,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,198評論 6 388
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著呈昔,像睡著了一般挥等。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上堤尾,一...
    開封第一講書人閱讀 51,165評論 1 299
  • 那天肝劲,我揣著相機(jī)與錄音,去河邊找鬼郭宝。 笑死辞槐,一個胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的粘室。 我是一名探鬼主播榄檬,決...
    沈念sama閱讀 40,052評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼衔统!你這毒婦竟也來了鹿榜?” 一聲冷哼從身側(cè)響起先朦,我...
    開封第一講書人閱讀 38,910評論 0 274
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎犬缨,沒想到半個月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體棉浸,經(jīng)...
    沈念sama閱讀 45,324評論 1 310
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡怀薛,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,542評論 2 332
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現(xiàn)自己被綠了迷郑。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片枝恋。...
    茶點(diǎn)故事閱讀 39,711評論 1 348
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖嗡害,靈堂內(nèi)的尸體忽然破棺而出焚碌,到底是詐尸還是另有隱情,我是刑警寧澤霸妹,帶...
    沈念sama閱讀 35,424評論 5 343
  • 正文 年R本政府宣布十电,位于F島的核電站,受9級特大地震影響叹螟,放射性物質(zhì)發(fā)生泄漏鹃骂。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,017評論 3 326
  • 文/蒙蒙 一罢绽、第九天 我趴在偏房一處隱蔽的房頂上張望畏线。 院中可真熱鬧,春花似錦良价、人聲如沸寝殴。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,668評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽蚣常。三九已至,卻和暖如春袖外,著一層夾襖步出監(jiān)牢的瞬間史隆,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 32,823評論 1 269
  • 我被黑心中介騙來泰國打工曼验, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留泌射,地道東北人。 一個月前我還...
    沈念sama閱讀 47,722評論 2 368
  • 正文 我出身青樓鬓照,卻偏偏與公主長得像熔酷,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子豺裆,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,611評論 2 353

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