Netty 框架總結(jié)「ChannelHandler 及 EventLoop」

學習了一段時間的 Netty融虽,將重點與學習心得總結(jié)如下,本文主要總結(jié)ChannelHandler 及 EventLoop 的知識點和基本用法灼芭,本文章節(jié)排序參照《Netty in Action》的章節(jié)排序有额。

以下內(nèi)容主要參考「并發(fā)編程網(wǎng)」的 《Netty in Action》中文版 以及《Netty in Action》原版圖書,輔助參考 Essential Netty in Action 《Netty 實戰(zhàn)(精髓)》 以及 Netty 官網(wǎng)的 Netty 4.1 JavaDoc

6. ChannelHandler 和 ChannelPipeline

一個 Channel 正常的生命周期如下圖所示谆吴。隨著狀態(tài)發(fā)生變化倒源,相應(yīng)的 event 產(chǎn)生。這些 event 被轉(zhuǎn)發(fā)到 ChannelPipeline 中的 ChannelHandler 來采取相應(yīng)的操作句狼。

Channel狀態(tài)模型

6.1 ChannelHandler

ChannelHandler 有兩個重要的子接口:

  • 「ChannelInboundHandler」處理輸入數(shù)據(jù)和所有類型的狀態(tài)變化
  • 「ChannelOutboundHandler」處理輸出數(shù)據(jù)笋熬,可以攔截所有操作

6.1.1 ChannelInboundHandler

下表列出接口 ChannelInboundHandler 的方法。當收到數(shù)據(jù)或相關(guān) Channel 的狀態(tài)改變時腻菇,這些方法被調(diào)用胳螟,這些方法和Channel的生命周期密切相關(guān)启具。

方法 描述
channelRegistered 當一個Channel注冊到EventLoop上层宫,可以處理I/O時被調(diào)用
channelUnregistered 當一個Channel從它的EventLoop上解除注冊,不再處理I/O時被調(diào)用
channelActive 當Channel變成活躍狀態(tài)時被調(diào)用蛛勉;Channel是連接/綁定丘薛、就緒的
channelInactive 當Channel離開活躍狀態(tài)嘉竟,不再連接到某個遠端時被調(diào)用
channelReadComplete 當Channel上的某個讀操作完成時被調(diào)用
channelRead 當從Channel中讀數(shù)據(jù)時被調(diào)用

6.1.2 ChannelOutboundHandler

輸出的操作和數(shù)據(jù)由 ChannelOutBoundHandler 處理。它的方法可以被 Channel洋侨,ChannelPipeline 和 ChannelHandlerContext 調(diào)用舍扰,子接口 ChannelOutboundHandler 的主要方法如下:

方法 描述
bind(ChannelHandlerContext,SocketAddress,ChannelPromise) 請求綁定 Channel 到一個本地地址
connect(ChannelHandlerContext, SocketAddress,SocketAddress,ChannelPromise) 請求連接 Channel 到遠端
disconnect(ChannelHandlerContext, ChannelPromise) 請求從遠端斷開 Channel
close(ChannelHandlerContext,ChannelPromise) 請求關(guān)閉 Channel
deregister(ChannelHandlerContext, ChannelPromise) 請求 Channel 從它的 EventLoop 上解除注冊
read(ChannelHandlerContext) 請求從 Channel 中讀更多的數(shù)據(jù)
flush(ChannelHandlerContext) 請求通過 Channel 刷隊列數(shù)據(jù)到遠端
write(ChannelHandlerContext,Object, ChannelPromise) 請求通過 Channel 寫數(shù)據(jù)到遠端

6.1.3 ChannelHandler 適配器類

ChannelInboundHandlerAdapter 和 ChannelOutboundHandlerAdapter 這兩個適配器類分別提供了 ChannelInboundHandler 和 ChannelOutboundHandler 的基本實現(xiàn),它們繼承了共同的父接口 ChannelHandler 的方法希坚,擴展了抽象類 ChannelHandlerAdapter边苹。

ChannelHandlerAdapter類層級關(guān)系
  • ChannelHandlerAdapter 提供了工具方法 isSharable()。如果類實現(xiàn)帶 @Sharable 注解裁僧,那么這個方法就會返回 true个束,意味著這個對象可以被添加到多個 ChannelPipeline 中。

  • ChannelInboundHandlerAdapter 和 ChannelOutboundHandlerAdapter 中的方法調(diào)用相關(guān) ChannelHandlerContext 中的等效方法聊疲,因此將事件轉(zhuǎn)發(fā)到管道中的下一個ChannelHandler茬底。

6.1.4 ChannelFuture 和 ChannelPromise

  • ChannelPromise 是 ChannelFuture 的子接口
  • 而 ChannelFuture 是不可變對象
  • ChannelPromise 定義了可寫的方法,比如 setSuccess(), setFailure()

6.1.5 釋放資源

1. 輸入方向「Inbound」
當一個 ChannelInboundHandler 實現(xiàn)類重寫 channelRead() 方法時售睹,它要負責釋放 ByteBuf 相關(guān)的內(nèi)存桩警。可使用 Netty 提供的工具方法:

    ReferenceCountUtil.release(「ByteBuf 的對象」)

更簡單的昌妹,可使用子類 SimpleChannelInboundHandler 捶枢,一條消息在被 ChannelRead0() 讀取后,會被自動釋放資源飞崖,此時任何對消息的引用都會變成無效烂叔,所以不能保存這些引用待后來使用。

2. 輸出方向「Outbound」
在輸出方向固歪,如果處理一個 write() 操作并且丟棄一條消息(沒有寫入 Channel)蒜鸡,就應(yīng)該負責釋放這條消息胯努。

@ChannelHandler.Sharable public 
class DiscardOutboundHandler extends ChannelOutboundHandlerAdapter {

@Override
public void write(ChannelHandlerContext ctx, Object msg, ChannelPromise promise) {
    ReferenceCountUtil.release(msg);  //使用 ReferenceCountUtil.release(...) 釋放資源
    promise.setSuccess();  //通知 ChannelPromise 數(shù)據(jù)已經(jīng)被處理
}

如果一個消息被“消費”或者丟棄,沒有送到 ChannelPipeline 中的下一個 ChannelOutboundHandler逢防,用戶就要負責調(diào)用 ReferenceCountUtil.release()叶沛。如果消息到達了真正的傳輸層,在它被寫到 Socket 中或者 Channel 關(guān)閉時忘朝,會被自動釋放灰署,用戶不用管。

6.2 ChannelPipeline 接口

  • 每個新創(chuàng)建的 Channel 都會分配一個新的 ChannelPipeline局嘁,Channel 不可以更換或解除當前的 ChannelPipeline溉箕,在 Netty 組件的整個生命周期中這個關(guān)系是固定的。

  • 一個 ChannelPipeline 可看成是一串 ChannelHandler 實例悦昵,攔截穿過 Channel 的輸入輸出 event肴茄。

  • 根據(jù)來源,一個 event 可以被一個 ChannelInboundHandler 或 ChannelOutboundHandler 處理但指。接下來寡痰,通過調(diào)用 ChannelHandlerContext 的方法,event 會被轉(zhuǎn)發(fā)到下一個同類型的 handler棋凳。

6.2.1 ChannelHandlerContext

  • 通過 ChannelHandlerContext氓癌,一個 handler 可以通知 ChannelPipeline 中的下一個ChannelHandler,甚至動態(tài)改動下一個ChannelHandler 所屬的 ChannelPipeline贫橙。

  • ChannelPipeline 主要由一系列 ChannelHandler 組成的。ChannelPipeline 提供在 ChannelPipeline 中傳送 event 的方法反粥。

  • ChannelHandlerContext 的一些方法和其他類(Channel 和 ChannelPipeline)的方法名字相似卢肃,但是 ChannelHandlerContext 的方法采用了更短的 event 傳遞路程。我們應(yīng)該盡可能利用這一點來實現(xiàn)最好的性能才顿。

  • 如果你在 Channel 或者 ChannelPipeline 實例上調(diào)用這些方法莫湘,它們的調(diào)用會穿過整個 pipeline。而在 ChannelHandlerContext 上調(diào)用的同樣的方法郑气,僅僅從當前 ChannelHandler 開始幅垮,走到 pipeline 中下一個可以處理這個 event 的 ChannelHandler。

ChannelPipeline 和 ChannelHandlers

「本節(jié)參考」 第六章 ChannelHandler 和 ChannelPipeline

7. EventLoop 和 EventLoopGroup

7.1 Java 基本的線程池模式

  • 從池中空閑的線程中選出一個尾组,分配一個提交的task「一個Runnable的實現(xiàn)」
  • 當task完成忙芒,線程返回池中,等待復用「下一次task分配」

7.2 EventLoop「事件循環(huán)」

  • EventLoop 始終由一個線程驅(qū)動
  • 一個 EventLoop 可以被指派來服務(wù)多個 Channel
  • 一個 Channel 只擁有一個 EventLoop

task (Runnable或Callable) 可以直接提交到 EventLoop 實現(xiàn)即刻或者延后執(zhí)行讳侨。根據(jù)配置和可用的CPU核呵萨,可以創(chuàng)建多個 EventLoop 來優(yōu)化資源利用。

一個 event 的本質(zhì)決定了它將如何被處理跨跨;它可能從網(wǎng)絡(luò)協(xié)議棧傳送數(shù)據(jù)到你的應(yīng)用潮峦,或者反過來,或者做一些完全不一樣的事情。但是 event 處理邏輯必須足夠通用和靈活忱嘹,來對付所有可能的情況嘱腥。

所以,在 Netty 4拘悦,所有的 I/O 操作和 event 都是由分配給 EventLoop 的那一個 Thread 來處理的齿兔。Netty 4 采用的線程模型,在同一個線程的 EventLoop 中處理所有發(fā)生的事窄做。

7.3 EventLoopGroup

  • EventLoopGroup 負責分配 EventLoop 到新創(chuàng)建的 Channel
  • 異步實現(xiàn)只用了很少 EventLoop愧驱,這幾個 EventLoop 被所有 Channel 共享
  • 一但 Channel 被指派了一個 EventLoop,在它的整個生命周期過程中椭盏,都會用這個 EventLoop
針對非阻塞傳輸?shù)腅ventLoop分配

為 Channel 的 I/O 和 event 提供服務(wù)的 EventLoop 都包含在一個 EventLoopGroup 中组砚。EventLoop 創(chuàng)建和分配的方式根據(jù)傳輸實現(xiàn)的不同而有所不同。

異步實現(xiàn)只用了很少幾個 EventLoop(和它們關(guān)聯(lián)的線程)掏颊,在目前 Netty 的模型中糟红,這幾個 EventLoop 被所有 Channel 共享。這讓很多 Channel 被最少數(shù)量的線程服務(wù)乌叶,而不是每個 Channel 分配一個線程盆偿。

EventLoopGroup 負責分配一個 EventLoop 到每個新創(chuàng)建的 Channel。在目前的實現(xiàn)中准浴,采用循環(huán) (round-robin) 策略可以滿足一個平衡的分配事扭,同一個 Eventloop 還可能會被分配到多個 Channel。

「本節(jié)參考」 第七章 EventLoop和線程模型

參考鏈接

  1. 《Netty in Action》中文版
  2. Essential Netty in Action 《Netty 實戰(zhàn)(精髓)》
  3. Netty 4.1 JavaDoc
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末乐横,一起剝皮案震驚了整個濱河市求橄,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌葡公,老刑警劉巖罐农,帶你破解...
    沈念sama閱讀 216,544評論 6 501
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異催什,居然都是意外死亡涵亏,警方通過查閱死者的電腦和手機,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,430評論 3 392
  • 文/潘曉璐 我一進店門蒲凶,熙熙樓的掌柜王于貴愁眉苦臉地迎上來气筋,“玉大人,你說我怎么就攤上這事旋圆●汕模” “怎么了?”我有些...
    開封第一講書人閱讀 162,764評論 0 353
  • 文/不壞的土叔 我叫張陵臂聋,是天一觀的道長光稼。 經(jīng)常有香客問我或南,道長,這世上最難降的妖魔是什么艾君? 我笑而不...
    開封第一講書人閱讀 58,193評論 1 292
  • 正文 為了忘掉前任采够,我火速辦了婚禮,結(jié)果婚禮上冰垄,老公的妹妹穿的比我還像新娘蹬癌。我一直安慰自己,他們只是感情好虹茶,可當我...
    茶點故事閱讀 67,216評論 6 388
  • 文/花漫 我一把揭開白布逝薪。 她就那樣靜靜地躺著,像睡著了一般蝴罪。 火紅的嫁衣襯著肌膚如雪董济。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 51,182評論 1 299
  • 那天要门,我揣著相機與錄音虏肾,去河邊找鬼。 笑死欢搜,一個胖子當著我的面吹牛封豪,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播炒瘟,決...
    沈念sama閱讀 40,063評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼吹埠,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了疮装?” 一聲冷哼從身側(cè)響起藻雌,我...
    開封第一講書人閱讀 38,917評論 0 274
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎斩个,沒想到半個月后,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體驯杜,經(jīng)...
    沈念sama閱讀 45,329評論 1 310
  • 正文 獨居荒郊野嶺守林人離奇死亡受啥,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 37,543評論 2 332
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現(xiàn)自己被綠了鸽心。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片滚局。...
    茶點故事閱讀 39,722評論 1 348
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖顽频,靈堂內(nèi)的尸體忽然破棺而出藤肢,到底是詐尸還是另有隱情,我是刑警寧澤糯景,帶...
    沈念sama閱讀 35,425評論 5 343
  • 正文 年R本政府宣布嘁圈,位于F島的核電站省骂,受9級特大地震影響,放射性物質(zhì)發(fā)生泄漏最住。R本人自食惡果不足惜钞澳,卻給世界環(huán)境...
    茶點故事閱讀 41,019評論 3 326
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望涨缚。 院中可真熱鬧轧粟,春花似錦、人聲如沸脓魏。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,671評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽茂翔。三九已至混蔼,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間檩电,已是汗流浹背拄丰。 一陣腳步聲響...
    開封第一講書人閱讀 32,825評論 1 269
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留俐末,地道東北人料按。 一個月前我還...
    沈念sama閱讀 47,729評論 2 368
  • 正文 我出身青樓,卻偏偏與公主長得像卓箫,于是被迫代替她去往敵國和親载矿。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 44,614評論 2 353

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