Netty 源码(ChannelHandler 死磕)
精進(jìn)篇:netty源碼死磕5? - 揭開(kāi) ChannelHandler 的神秘面紗
目錄
1. 前言
2. Handler在經(jīng)典Reactor中的角色
3. Handler在Netty中的坐標(biāo)位置
4. Netty中Handler的類(lèi)型
1.1. ChannelInboundHandler入站處理器
1.2. ChannelOutboundHandler出站處理器
5. 揭開(kāi)Pipeline的神秘面紗
6. Handler的上下文環(huán)境
7. Handler的注冊(cè)
7.1. 第一步:包裹
7.2. 加入鏈表并注冊(cè)完成回調(diào)事件
7.3. 回調(diào)添加完成事件
8. 小結(jié)
1. 前言
Reactor模式是Netty的基礎(chǔ)和靈魂,掌握了經(jīng)典的Reactor模式實(shí)現(xiàn),徹底掌握Netty就事半功倍了?!?/font>Reactor模式(netty源碼死磕3)》對(duì)Reactor模式的經(jīng)典實(shí)現(xiàn),進(jìn)行了詳細(xì)介紹。作為本文的閱讀準(zhǔn)備,可以去溫習(xí)一下。
Reactor模式的兩個(gè)重要的組件,一個(gè)是Reactor反應(yīng)器,在Netty中的對(duì)應(yīng)的實(shí)現(xiàn)是EventLoop,在文章《EventLoop(netty源碼死磕4)》中,已經(jīng)有了非常詳細(xì)的介紹。
此文聚焦于Reactor模式的另一個(gè)重要的組成部分Handler。
2. Handler在經(jīng)典Reactor中的角色
在Reactor經(jīng)典模型中,Reactor查詢到NIO就緒的事件后,分發(fā)到Handler,由Handler完成NIO操作和計(jì)算的操作。
Handler主要的操作為Channel緩存讀、數(shù)據(jù)解碼、業(yè)務(wù)處理、寫(xiě)Channel緩存,然后由Channel(代表client)發(fā)送到最終的連接終端。
3. Handler在Netty中的坐標(biāo)
經(jīng)典的Reactor模式,更多在于演示和說(shuō)明,僅僅是有一種濃縮和抽象。
由于Netty更多用于生產(chǎn),在實(shí)際開(kāi)發(fā)中的業(yè)務(wù)處理這塊,主要通過(guò)Handler來(lái)實(shí)現(xiàn),所以Netty中在Handler的組織設(shè)計(jì)這塊,遠(yuǎn)遠(yuǎn)比經(jīng)典的Reactor模式實(shí)現(xiàn),要紛繁復(fù)雜得多。
在分析Handler之前,首先回顧一下Netty中的Channel。在《EventLoop(netty源碼死磕4)》中,已經(jīng)有詳細(xì)的說(shuō)明。一個(gè)Netty Channel對(duì)應(yīng)于一個(gè)Client連接,內(nèi)部封裝了一個(gè)Java NIO SelectableChannel 可查詢通道。
再回到Handler。
Hander的根本使命,就是處理Channel的就緒事件,根據(jù)就緒事件,完成NIO處理和業(yè)務(wù)操作。比方Channel讀就緒時(shí),Hander就開(kāi)始讀;Channel寫(xiě)就緒時(shí),Hander就開(kāi)始寫(xiě)。
4. Netty中Handler的類(lèi)型
從應(yīng)用程序開(kāi)發(fā)人員的角度來(lái)看,Netty的主要組件是ChannelHandler,所以,對(duì)ChannelHandler的分類(lèi),也是從應(yīng)用開(kāi)發(fā)的角度來(lái)的。
從應(yīng)用程序開(kāi)發(fā)人員的角度來(lái)看,數(shù)據(jù)有入站和出站兩種類(lèi)型。
這里的出站和入站,不是網(wǎng)絡(luò)通信方面的入站和出站。而是相對(duì)于Netty Channel與Java NIO Channel而言的。
數(shù)據(jù)入站,指的是數(shù)據(jù)從底層的Java NIO channel到Netty的Channel。數(shù)據(jù)出站,指的是通過(guò)Netty的Channel來(lái)操作底層的 Java NIO chanel。
從入站和出戰(zhàn)的角度出發(fā),Netty中的ChannelHandler主要由兩種類(lèi)型,ChannelInboundHandler和ChannelOutboundHandler。
1.1. ChannelInboundHandler入站處理器
當(dāng)Java NIO事件進(jìn)站到Channel時(shí),產(chǎn)生一的一系列事件將由ChannelHandler所對(duì)應(yīng)的API處理。
當(dāng)查詢到Java NIO底層Channel的就緒事件時(shí),通過(guò)一系列的ChannelInboundHandler處理器,完成底層就緒事件的處理。比方說(shuō)底層連接建立事件、底層連接斷開(kāi)事件、從底層讀寫(xiě)就緒事件等等。
啰嗦一下,入站(inbound)處理通常由底層Java NIO channel觸發(fā),主要事件如下:
1. 注冊(cè)事件 fireChannelRegistered。
2. 連接建立事件 fireChannelActive。
3. 讀事件和讀完成事件 fireChannelRead、fireChannelReadComplete。
4. 異常通知事件 fireExceptionCaught。
5. 用戶自定義事件 fireUserEventTriggered。
6. Channel 可寫(xiě)狀態(tài)變化事件 fireChannelWritabilityChanged。
7. 連接關(guān)閉事件 fireChannelInactive。
1.2. ChannelOutboundHandler出站處理器
當(dāng)需要Netty Channel需要操作Java NIO底層Channel時(shí),通過(guò)一系列的ChannelOutboundHandler處理器,完成底層操作。比方說(shuō)建立底層連接、斷開(kāi)底層連接、從底層Java NIO通道讀入、寫(xiě)入底層Java NIO通道等。ChannelOutboundHandler是一個(gè)接口,主要操作如下圖所示:
啰嗦一下,出站(inbound) Handler通常是Netty channel操作底層Java NIO channel,主要操作如下:
1. 端口綁定 bind。
2. 連接服務(wù)端 connect。
3. 寫(xiě)事件 write。
4. 刷新時(shí)間 flush。
5. 讀事件 read。
6. 主動(dòng)斷開(kāi)連接 disconnect。
7. 關(guān)閉 channel 事件 close。
至此,Netty中的兩大處理器的類(lèi)型,就已經(jīng)說(shuō)得很清楚了。
再說(shuō)說(shuō)Handler和Channel的關(guān)系。
打個(gè)比方,如果Hander是太陽(yáng)系的行星,那么Channel就是太陽(yáng)系的恒星。Hander的服務(wù)對(duì)象和公轉(zhuǎn)的軸心,就是Channel。
這可能是最為不恰當(dāng)?shù)囊粋€(gè)比方,但是說(shuō)的是事實(shí)。
5. 揭開(kāi)Pipeline的神秘面紗
一個(gè)Channel在數(shù)量上,肯定不止擁有一個(gè)Handler。 如何將雜亂無(wú)章的Handler,有序的組織起來(lái)呢?
來(lái)了一個(gè)Handler的裝配器——Pipeline。
Pipeline是何方神圣呢?
先揭一下神秘面紗:
Netty中, 使用一個(gè)雙向鏈表,將屬于一個(gè)Channel的所有Handler組織起來(lái),并且給這個(gè)雙向鏈表封裝在一個(gè)類(lèi)中,再給這個(gè)類(lèi)取了一個(gè)非常牛逼的名字,叫做ChannelPipeline。
為什么這個(gè)名字很牛逼呢?
實(shí)際上這里用了Java中一種非常重要的設(shè)計(jì)模式,Pipeline設(shè)計(jì)模式。后面將用專(zhuān)門(mén)的文章,來(lái)介紹這種牛逼模式。
回到主題:
一個(gè)Channel,僅僅一個(gè)ChannelPipeline。該pipeline在Channel被創(chuàng)建的時(shí)候創(chuàng)建。ChannelPipeline相當(dāng)于是ChannelHandler的容器,它包含了一個(gè)ChannelHander形成的列表,且所有ChannelHandler都會(huì)注冊(cè)到ChannelPipeline中。
6. Handler的上下文環(huán)境
在Netty的設(shè)計(jì)中,Handler是無(wú)狀態(tài)的,不保存和Channel有關(guān)的信息。打個(gè)不恰當(dāng)?shù)谋确?#xff0c;Handler就像國(guó)際雇傭軍一樣,誰(shuí)給錢(qián),給誰(shuí)打仗。Handler的目標(biāo),是將自己的處理邏輯做得很完成,可以給不同的Channel使用。
與之不同的是,Pipeline是有狀態(tài)的,保存了Channel的關(guān)系。
于是乎,Handler和Pipeline之間,需要一個(gè)中間角色,把他們聯(lián)系起來(lái)。這個(gè)中間角色是誰(shuí)呢?
它就是——ChannelHandlerContext 。
所以,ChannelPipeline 中維護(hù)的,是一個(gè)由 ChannelHandlerContext 組成的雙向鏈表。這個(gè)鏈表的頭是 HeadContext, 鏈表的尾是 TailContext。而無(wú)狀態(tài)的Handler,作為Context的成員,關(guān)聯(lián)在ChannelHandlerContext 中。在對(duì)應(yīng)關(guān)系上,每個(gè) ChannelHandlerContext 中僅僅關(guān)聯(lián)著一個(gè) ChannelHandler。
我們繼續(xù)用源碼說(shuō)話。
Context的雙向鏈表的主要代碼,在 AbstractChannelHandlerContext類(lèi)中。該類(lèi)主要包含一個(gè)雙向鏈表節(jié)點(diǎn)的前置和后置節(jié)點(diǎn)引用 prev、next,以及數(shù)據(jù)引用 handler,相當(dāng)于鏈表數(shù)據(jù)結(jié)構(gòu)中的 Node 節(jié)點(diǎn)。
部分關(guān)鍵源碼節(jié)選如下:
7. Handler的注冊(cè)
Handler是如何注冊(cè)到Pipeline中的呢?
1.3. 第一步:包裹
加入到Pipeline之前,在Pipeline的基類(lèi)DefaultChannelPipeline中,首先對(duì)Handler進(jìn)行包裹。
代碼如下:
1.4. 加入鏈表并注冊(cè)完成回調(diào)事件
1. 構(gòu)建了 AbstractChannelHandlerContext 節(jié)點(diǎn),并加入到了鏈表尾部。
2. 如果 channel 尚未注冊(cè)到 EventLoop,就添加一個(gè)任務(wù)到 PendingHandlerCallback 上,后續(xù)channel 注冊(cè)完畢,再調(diào)用 ChannelHandler.handlerAdded。
3. 如果已經(jīng)注冊(cè),馬上調(diào)用 callHandlerAdded0 方法來(lái)執(zhí)行 ChannelHandler.handlerAdded 注冊(cè)完成的回調(diào)函數(shù)。
代碼如下:
1.5. 回調(diào)添加完成事件
添加完成后,執(zhí)行回調(diào)方法如下:
會(huì)執(zhí)行handler的handlerAdded 方法,這是一個(gè)回調(diào)方法。添加完成后的回調(diào)代碼,基本上寫(xiě)在這里。
8. 小結(jié)
至此,牛逼的Netty Handler和Netty Reactor 介紹完了。
對(duì)于Pipeline模式和基于Pipeline的Netty 入站和出站的事件傳輸機(jī)制,【瘋狂創(chuàng)客圈】在后面的系列死磕文章,會(huì)做一個(gè)非常精彩的介紹。
無(wú)編程不創(chuàng)客,無(wú)案例不學(xué)習(xí)。瘋狂創(chuàng)客圈,一大波高手正在交流、學(xué)習(xí)中!
瘋狂創(chuàng)客圈 Netty 死磕系列 10多篇深度文章: 【博客園 總?cè)肟?/strong>】? QQ群:104131248
轉(zhuǎn)載于:https://www.cnblogs.com/crazymakercircle/p/9853586.html
總結(jié)
以上是生活随笔為你收集整理的Netty 源码(ChannelHandler 死磕)的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問(wèn)題。
- 上一篇: 阿里云Elasticsearch 智能化
- 下一篇: Algs4-1.4.7统计算术运算与比较