netty源碼分析_帶你搞懂ChannelHandler事件傳播順序

明確關(guān)鍵點:

要搞懂事件在多個ChannelHandler間的傳播順序棋凳,有兩個關(guān)鍵點需要明確

1.pipeline初始化時拦坠,會創(chuàng)建兩個哨兵Handler,即HeadContext剩岳、TailContext贞滨,我們添加的Handler處于這兩個哨兵Handler之間,HeadContext可以是入站事件傳播的起點拍棕,一定是出站事件傳播的終點

TailContext可以是出站事件傳播的起點(為啥分為"可以是"與"一定是"晓铆,下文會有說明)

2.事件的傳播起點、方向绰播、目標(biāo):

入站事件傳播的起點為當(dāng)前Handler或者HeadContext骄噪,方向為next,也就是往下一個蠢箩,目標(biāo)是InboundHandler

出站事件傳播的起點為當(dāng)前Handler或者TailContext链蕊,方向為prev,也就是往回一個忙芒,目標(biāo)是OutboundHandler

Pipeline初始化示弓,組裝Handler鏈:

下面以這兩點為主線進(jìn)行剖析,服務(wù)端的啟動入口大家都知道呵萨,是Bootstrap.connect

connect方法首先會通過反射創(chuàng)建一個Channel奏属,這個過程如下圖:

在Channel的構(gòu)造方法中,會為該Channel初始化一個DefaultChannelPipeline潮峦,這個類的構(gòu)造方法如下:?

HeadContext囱皿、TailContext,就是這個pipeline中初始的兩個Handler忱嘹,也就是上面說的哨兵Handler嘱腥,其中TailContext是InboundHandler,HeadContext既是InboundHandler拘悦,也是OutboundHandler?

后續(xù)我們在調(diào)用pipeline.addLast()方法添加的Handler都會處于HeadContext與TailContext之間齿兔,比如在我們添加了ByteToMessageDecoder(子類)、MessagetobyteEncoder(子類)础米、BizHandler三個Handler之后分苇,Handler鏈就是這樣(注意,Handler之間是雙向連接的屁桑,從左往右是next方向医寿,從右往左是prev方向):?

read事件傳播:

好了,Handler鏈已經(jīng)組裝好了蘑斧,接下來就是事件傳播了

我們以讀寫事件為例靖秩,在netty中须眷,read事件由netty幫我們傳播,write事件由我們自己傳播

先看read事件沟突,我們得找到read事件的源頭花颗,Netty中,channel的所有IO事件由EventLoop處理事扭,所以我們將視線轉(zhuǎn)移到NioEventLoop中捎稚,NioEventLoop的run方法里就干了兩件事情乐横,1是輪詢并處理selector事件求橄,2是處理taskQueue中任務(wù),我們的重點放在1葡公,從run開始看起罐农,直到出現(xiàn)read事件傳播,大致流程如下:

最后調(diào)用pipeline.fireChannelRead進(jìn)行read事件傳播催什,注意了涵亏!在netty中,事件傳播有兩類方法

1.Pipeline.*蒲凶,比如fireChannelRead傳播讀事件气筋、write傳播寫事件(channel.fire*最終也會走到pipeline.fire*)

2.ChannelHandlerContext.*,比如fireChannelRead傳播讀事件旋圆、write傳播寫事件

這兩類方法的唯一區(qū)別傳播的起點不一樣宠默,前者的起點是HeadContext(入站事件起點)、或者TailContext(出站事件起點)灵巧,后者的起點是當(dāng)前Handler搀矫;

好了,我們接著看pipeline.fireChannelRead方法邏輯刻肄,方法體如下:?

headH壳颉!敏弃!沒錯卦羡,就是HeadContext,pipeline中第一個Handler麦到,通過Pipeline.*方法傳播入站事件時绿饵,就以HeadContext為起點,它會繼續(xù)把讀事件往下傳播

這個do while循環(huán)的意思很明確隅要,一直往next方向找蝴罪,直到第一個InboundHandler,調(diào)用其channelRead方法步清,對于我們上面的Handler鏈要门,就會首先找到ByteToMessageDecoder虏肾;ByteToMessageDecoder的channelRead方法會先調(diào)用子類的decode方法,然后繼續(xù)傳播事件欢搜,代碼如下:為方便理解封豪,我略去了一些內(nèi)容,重點關(guān)注我紅框標(biāo)出的代碼?

fireChannelRead方法:

它調(diào)用的是ChannelHandlerContext.fire*炒瘟,前面說了吹埠,這類方法的起點是當(dāng)前Handler,傳播的方向和上面貼的邏輯一致疮装,我再貼過來:

?

依然是往next方向缘琅,找到第一個InboundHandler,也就是我們的BizHandler

通常情況下廓推,BizHandler的channelRead方法中刷袍,我們不會再繼續(xù)往下傳播read事件了,read事件到此結(jié)束樊展,所以本次read事件的傳播過程就是這樣:

1.NioEventLoop輪詢出就緒的read事件后呻纹,調(diào)用Pipeline.fireChannelRead方法傳播事件

2.Pipeline.fireChannelRead方法會以HeadContext為起點,向next方向找InboundHandler专缠,在本例中雷酪,也就是ByteToMessageDecoder

3.在解碼出Message后,ByteToMessageDecoder會調(diào)用ChannelHandlerContext.fireChannelRead方法傳播事件

4.該方法會以當(dāng)前Handler為起點涝婉,向next方向找InboundHandler哥力,在本例中,也就是BizHandler

5.BizHandler中我們一般不會繼續(xù)傳播讀事件嘁圈,讀事件結(jié)束

write事件傳播:

再來看看write事件省骂,原理與上面的read事件類似,區(qū)別就是方向+目標(biāo)不同最住!

入站事件的方向是next方向钞澳,目標(biāo)是InboundHandler

出站事件的方向與入站事件相反,是prev方向涨缚,也就是往回傳播轧粟,目標(biāo)是OutboundHandler

正常情況下,write事件由我們開發(fā)者觸發(fā)脓魏,分為兩種方式:?

對應(yīng)我們前面說的兰吟,事件傳播的兩類方法:Pipeline.write、ChannelHandlerContext.write

拿ctx.channel().write來說茂翔,流程大致如下:

?

Pipeline.write方法如下:

?tail;彀!珊燎!沒錯惭嚣,就是TailContext遵湖,pipeline的最后一個Handler,通過Pipeline.*方法傳播出站事件時晚吞,就以TailContext為起點延旧,它會繼續(xù)把讀事件往下傳播

?

可以看到,傳播方向與read相反槽地,往回找前一個OutboundHandler迁沫,我把Handler鏈再貼過來?

TailContext先找到BizHandler,發(fā)現(xiàn)不是OutboundHandler捌蚊,再找到MessageToByteEncoder集畅,發(fā)現(xiàn)是OutboundHandler,調(diào)用其write方法:

先調(diào)用子類的encode方法逢勾,再調(diào)用ChannelHandlerContext.write方法牡整,繼續(xù)往prev方向找前一個OutboundHandler藐吮,也就是HeadContext溺拱,HeadContext會調(diào)用底層unsafe的write方法往netty的寫緩沖區(qū)寫入字節(jié)流,對于我們來說谣辞,write事件到此就結(jié)束了迫摔,所以本次write事件的傳播過程大致如下:

1.BizHandler中調(diào)用ctx.channel().write

2.以TailContext為起點,prev為方向泥从,OutboundHandler為目標(biāo)句占,查找下一個符合要求的Handler,也就是MessageToByteEncoder

3.MessageToByteEncoder調(diào)用完子類的encode方法將消息編碼后躯嫉,繼續(xù)調(diào)用ctx.write傳播事件

4.ctx.write方法以當(dāng)前Handler為起點纱烘,prev為方向,OutboundHandler為目標(biāo)祈餐,查找下一個符合要求的Handler擂啥,也就是HeadContext

5.HeadContext調(diào)用底層的unsafe將消息寫入緩沖區(qū),寫事件結(jié)束

如果帆阳,在BizHandler中哺壶,調(diào)用的是ctx.write方法,那就不會走到TailContext了蜒谤,而是以當(dāng)前Handler為起點山宾,往后找到下一個滿足條件的Handler,也就是MessageToByteEncoder鳍徽,對本例來說资锰,效果一樣

如果在往pipeline中添加Handler時,把MessageToByteEncoder放到BizHandler后面阶祭,也就是這樣:?

你想一想绷杜,ctx.write和ctx.channel().write效果還一樣嗎翎猛?

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市接剩,隨后出現(xiàn)的幾起案子切厘,更是在濱河造成了極大的恐慌,老刑警劉巖懊缺,帶你破解...
    沈念sama閱讀 212,884評論 6 492
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件疫稿,死亡現(xiàn)場離奇詭異,居然都是意外死亡鹃两,警方通過查閱死者的電腦和手機遗座,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 90,755評論 3 385
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來俊扳,“玉大人途蒋,你說我怎么就攤上這事〔黾牵” “怎么了号坡?”我有些...
    開封第一講書人閱讀 158,369評論 0 348
  • 文/不壞的土叔 我叫張陵,是天一觀的道長梯醒。 經(jīng)常有香客問我宽堆,道長,這世上最難降的妖魔是什么茸习? 我笑而不...
    開封第一講書人閱讀 56,799評論 1 285
  • 正文 為了忘掉前任畜隶,我火速辦了婚禮,結(jié)果婚禮上号胚,老公的妹妹穿的比我還像新娘籽慢。我一直安慰自己,他們只是感情好猫胁,可當(dāng)我...
    茶點故事閱讀 65,910評論 6 386
  • 文/花漫 我一把揭開白布箱亿。 她就那樣靜靜地躺著,像睡著了一般杜漠。 火紅的嫁衣襯著肌膚如雪极景。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 50,096評論 1 291
  • 那天驾茴,我揣著相機與錄音盼樟,去河邊找鬼。 笑死锈至,一個胖子當(dāng)著我的面吹牛晨缴,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播峡捡,決...
    沈念sama閱讀 39,159評論 3 411
  • 文/蒼蘭香墨 我猛地睜開眼击碗,長吁一口氣:“原來是場噩夢啊……” “哼筑悴!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起稍途,我...
    開封第一講書人閱讀 37,917評論 0 268
  • 序言:老撾萬榮一對情侶失蹤阁吝,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后械拍,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體突勇,經(jīng)...
    沈念sama閱讀 44,360評論 1 303
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 36,673評論 2 327
  • 正文 我和宋清朗相戀三年坷虑,在試婚紗的時候發(fā)現(xiàn)自己被綠了甲馋。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 38,814評論 1 341
  • 序言:一個原本活蹦亂跳的男人離奇死亡迄损,死狀恐怖定躏,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情芹敌,我是刑警寧澤痊远,帶...
    沈念sama閱讀 34,509評論 4 334
  • 正文 年R本政府宣布,位于F島的核電站党窜,受9級特大地震影響拗引,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜幌衣,卻給世界環(huán)境...
    茶點故事閱讀 40,156評論 3 317
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望壤玫。 院中可真熱鬧豁护,春花似錦、人聲如沸欲间。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,882評論 0 21
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽猎贴。三九已至班缎,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間她渴,已是汗流浹背达址。 一陣腳步聲響...
    開封第一講書人閱讀 32,123評論 1 267
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留趁耗,地道東北人沉唠。 一個月前我還...
    沈念sama閱讀 46,641評論 2 362
  • 正文 我出身青樓,卻偏偏與公主長得像苛败,于是被迫代替她去往敵國和親满葛。 傳聞我的和親對象是個殘疾皇子径簿,可洞房花燭夜當(dāng)晚...
    茶點故事閱讀 43,728評論 2 351