netty學習系列三:pipeline原理與事件處理

零、目錄

一、pipeline整體關系簡述

二、Unsafe的作用

三、事件的分類及處理

四、pipeline中節點的添加和刪除

一、pipeline整體關系簡述

1、netty中的pipeline模型

當EventLoop的selector監聽到某Channel產生了就緒的IO事件,并調用socket API對就緒的IO事件進行操作后,需要將操作產生的“IO數據”或“操作結果”告知用戶進行相應的業務處理。

netty將因外部IO事件導致的Channel狀態變更(Channel被注冊到EventLoop中,Channel狀態變為可用,Channel讀取到IO數據...)或Channel內部邏輯操作(添加ChannelHandler...)抽象為不同的回調事件,并定義了pipeline對Channel的回調事件進行流式的響應處理。

用戶可在pipeline中添加多個事件處理器(ChannelHandler),并通過實現ChannelHandler中定義的方法,對回調事件進行定制化的業務處理。ChannelHandler也可以調用自身方法對Channel本身進行操作。

netty會保證“回調事件在ChannelHandler之間的流轉”及“Channel內部IO操作”由EventLoop線程串行執行,用戶也可以在ChannelHandler中使用自行構建的業務線程進行業務處理。

2、pipeline相關類的關系圖

pipeline相關類的關系圖

1)DefaultChannelPipeline:

事件處理流,是一個雙向鏈表結構,鏈表中節點元素為ChannelHandlerContext。

新的AbstractNioChannel創建時,會創建該Channel對應的DefaultChannelPipeline,用于處理該Channel對應的回調事件。

DefaultChannelPipeline創建時,會自動創建并向鏈表中添加兩個ChannelhandlerContext節點——head和tail。

pipeline的fireXXX()方法:回調事件的發起方法。會產生相應回調事件并直接調用ChannelHandlerContext.invokeXXX(head)方法將回調事件傳遞給pipeline的head節點。

2)ChannelHandlerContext:

事件處理器上下文,pipeline中的實際處理節點。

每個處理節點ChannelHandlerContext中包含一個具體的事件處理器ChannelHandler,同時ChannelHandlerContext中也綁定了對應的pipeline和Channel的信息,方便ChannelHandler進行調用。

AbstractChannelHandlerContext具體實現了ChannelHandlerContext接口的功能,并進行了相應擴展。

ChannelHandlerContext的fireXXX方法:回調事件的發起方法。會產生相應回調事件并將其交給pipeline中的下一個處理節點。此方法提供給用戶實現的ChannelHandler使用,用于將回調事件向pipeline中的下一個節點傳遞。

AbstractChannelHandlerContext的static invokeXXX(AbstractChannelHandlerContext next)方法:封裝next.invokeXXX()的邏輯并交給EventLoop的IO線程執行。

ChannelHandlerContext的invokeXXX()方法:回調事件執行方法。執行節點中事件處理器ChannelHandler的XXX方法,實際處理回調事件。

3)ChannelHandler:


ChannelHandler類結構

ChannelHandler(事件處理器接口),由ChannelInboundHandler接口和ChannelOutboundHandler接口繼承。

ChannelInboundHandler中定義了各個回調事件的回調方法,由用戶進行具體實現。

ChannelOutboundHandler中定義了方法進行Channel內部IO操作(Channel發起bind/connect/close操作,Channel監聽OP_READ,Channel寫IO數據...),供用戶在回調方法中使用。

ChannelInboundHandlerAdapter和ChannelOutboundHandlerAdapter為接口的默認實現類(其實沒干什么事),用戶通過繼承這兩個類來實現自己的業務處理邏輯。

二、Unsafe的作用

1、概述

Unsafe是Channel的內部類,一個Channel對應一個Unsafe。

Unsafe用于處理Channel對應網絡IO的底層操作。ChannelHandler處理回調事件時產生的相關網絡IO操作最終也會委托給Unsafe執行。

2、類繼承結構

Unsafe類繼承結構

Unsafe接口中定義了socket相關操作,包括SocketAddress獲取、selector注冊、網卡端口綁定、socket建連與斷連、socket寫數據。這些操作都和jdk底層socket相關。

NioUnsafe在Unsafe基礎上增加了幾個操作,包括訪問jdk的SelectableChannel、socket讀數據等。

NioByteUnsafe實現了與socket連接的字節數據讀取相關的操作。

NioMessageUnsafe實現了與新連接建立相關的操作。

3、讀/寫操作實現淺述

Unsafe的讀寫操作

三、事件的分類及處理

1、Inbound事件

用于描述因外部事件導致的Channel狀態變更。

Inbound事件列表

ChannelInboundHandler中定義了各個Inbound事件的回調方法。

2、Outbound事件

用于定義Channel能夠提供的IO操作。

outbound事件列表

ChannelOutboundHandler中定義了Channel提供的進行IO操作的方法。

3、發起并處理inbound事件

ChannelInboundInvoker接口定義了inbound事件的發起方法fireXXX(),用于發起一個inbound事件。

ChannelPipeline接口繼承了ChannelInboundInvoker,其實現類DefaultChannelPipeline因此具有發起inbound事件的功能。-->? 實際通過調用AbstractChannelHandlerContext的靜態invokeXXX()方法,產生并從pipeline的head節點開始接收事件。

ChannelHandlerContext接口繼承了ChannelInboundInvoker,其實現類AbstractChannelhandlerContext因此具有發起inbound事件的功能。 -->? 實際通過調用AbstractChannelHandlerContext的靜態invokeXXX()方法,從當前節點的下一個節點開始接收事件。

AbstractChannelHandlerContext類中定義了靜態invokeXXX(next)方法,用于在指定節點上開始接收inbound事件,并保證接收過程在EventLoop線程中執行。

AbstractChannelHandlerContext類中定義了invokeXXX()方法,用于實際接收inbound事件,并執行其ChannelHandler實現的事件回調方法。

對于inbound事件,因為需要進行業務邏輯處理,因此pipeline的head節點會執行fireXXX()方法將事件透傳給后面的用戶自己實現inbound處理節點,由用戶自己實現的ChannelHandler接收事件并回調執行業務邏輯。

inbound事件的發起與處理過程

fireChannelActive、fireChannelRead、fireChannelReadComplete等inbound事件的處理過程可以自己跟代碼看一下。(head節點對channelRead事件向后透傳。對channelActive和channelReadComplete事件,向后傳遞并產生了Channel.read()的outbound事件)


4、發起并處理outbound事件

ChannelOutboundInvoker接口定義了outbound事件的產生方法(register,bind,connect,read,write,flush...),用于描述Channel能夠提供的IO操作。

Channel接口繼承了ChannelOutboundInvoker,其實現類AbstractChannel因此具有執行outbound事件的功能。-->? 實際通過調用ChannelPipeline的對應方法實現。

ChannelPipeline接口繼承了ChannelOutboundInvoker,其實現類DefaultChannelPipeline因此具有執行outbound事件的功能。-->? 實際通過調用pipeline的tail節點的對應方法實現。

ChannelHandlerContext接口繼承了ChannelOutboundInvoker,其實現類AbstractChannelhandlerContext因此具有執行outbound事件的功能。-->? 實際從當前節點的前一個outbound處理節點開始接收事件,調用其invokeXXX()方法,并保證接收過程在EventLoop線程中執行。

AbstractChannelHandlerContext類中定義了invokeXXX()方法,用于實際接收outbound事件,并執行其ChannelHandler實現的事件執行方法。

對于outbound事件,因為和IO操作相關,最后會由pipeline中的head節點接收處理。head節點實現了ChannelHandler的事件執行方法,將實際的執行操作委托給Unsafe進行。

outbound事件的發起與處理過程

bind、connect、read、writeAndFlush等outbound事件的處理過程可以自己跟代碼看一下。(最終由head節點委托給Unsafe類執行相關IO操作)

5、head、tail節點的作用


pipeline

head節點:

head節點既是inBound處理節點,又是outBound處理節點。

head節點作為pipeline的頭結點開始接收并傳遞inbound事件。并作為pipeline的最后一環最終接收處理outbound事件(委托Unsafe進行outbound事件的相關IO操作)。

tail節點:

tail節點是inBound處理節點。

tail節點作為pipeline的第一環傳遞outbound事件,其實就是將outbound事件透傳到前一個outbound處理節點。并作為pipeline的最后一環最終接收inbound事件,大部分左右是終止inbound事件的傳播。

tail節點的exceptionCaught方法:若最終在用戶自定義的處理節點沒有捕獲處理異常,則在tail節點捕獲異常打印警告日志。

tail節點的channelRead方法:若Channel讀入的ByteBuf在流經pipeline過程中沒有被消費掉,最終流入了tail節點,則將該ByteBuf丟棄回收并打印警告日志。

四、pipeline中節點的添加和刪除

1、向pipeline中添加ChannelHandler的過程

從DefaultChannelPipeline.addLast(ChannelHandler... handlers)這行代碼開始跟吧

細節不想寫了,列一下大致流程:

1)checkMultiplicity(handler);? -->? 檢查要添加的handler是否被重復添加

2)newCtx= newContext(group,filterName(name,handler),handler);? -->? 創建節點

3)addLast0(newCtx);? --> 添加節點到pipeline的末尾(tail之前)

4)callHandlerAdded0(newCtx);? -->? 回調新建節點的handlerAdded(ctx)方法

2、將ChannelHandler從pipeline中移除的過程

從DefaultChannelPipeline.remove(ChannelHandler handler)這行代碼開始跟吧

細節不想寫了,列一下大致流程:

1)getContextOrDie(handler)? -->? 遍歷pipeline鏈表找到待刪除的接待對象

2)remove0(ctx)? --> 將節點從pipeline鏈表中移除

3)callHandlerRemoved0(ctx)? ? --> 回調移除節點的handlerRemoved(ctx)方法。


最后編輯于
?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市,隨后出現的幾起案子,更是在濱河造成了極大的恐慌,老刑警劉巖,帶你破解...
    沈念sama閱讀 230,825評論 6 546
  • 序言:濱河連續發生了三起死亡事件,死亡現場離奇詭異,居然都是意外死亡,警方通過查閱死者的電腦和手機,發現死者居然都...
    沈念sama閱讀 99,814評論 3 429
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人,你說我怎么就攤上這事。” “怎么了?”我有些...
    開封第一講書人閱讀 178,980評論 0 384
  • 文/不壞的土叔 我叫張陵,是天一觀的道長。 經常有香客問我,道長,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 64,064評論 1 319
  • 正文 為了忘掉前任,我火速辦了婚禮,結果婚禮上,老公的妹妹穿的比我還像新娘。我一直安慰自己,他們只是感情好,可當我...
    茶點故事閱讀 72,779評論 6 414
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著,像睡著了一般。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發上,一...
    開封第一講書人閱讀 56,109評論 1 330
  • 那天,我揣著相機與錄音,去河邊找鬼。 笑死,一個胖子當著我的面吹牛,可吹牛的內容都是我干的。 我是一名探鬼主播,決...
    沈念sama閱讀 44,099評論 3 450
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了?” 一聲冷哼從身側響起,我...
    開封第一講書人閱讀 43,287評論 0 291
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后,有當地人在樹林里發現了一具尸體,經...
    沈念sama閱讀 49,799評論 1 338
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內容為張勛視角 年9月15日...
    茶點故事閱讀 41,515評論 3 361
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發現自己被綠了。 大學時的朋友給我發了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 43,750評論 1 375
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖,靈堂內的尸體忽然破棺而出,到底是詐尸還是另有隱情,我是刑警寧澤,帶...
    沈念sama閱讀 39,221評論 5 365
  • 正文 年R本政府宣布,位于F島的核電站,受9級特大地震影響,放射性物質發生泄漏。R本人自食惡果不足惜,卻給世界環境...
    茶點故事閱讀 44,933評論 3 351
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧,春花似錦、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 35,327評論 0 28
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至,卻和暖如春,著一層夾襖步出監牢的瞬間,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 36,667評論 1 296
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人。 一個月前我還...
    沈念sama閱讀 52,492評論 3 400
  • 正文 我出身青樓,卻偏偏與公主長得像,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 48,703評論 2 380

推薦閱讀更多精彩內容