Netty原始碼學習3——Channel ,ChannelHandler,ChannelPipeline

2023-08-28 06:00:34

系列文章目錄和關於我

零丶引入

Netty原始碼學習2——NioEventLoop的執行中,我們學習了NioEventLoop是如何進行事件迴圈以及如何修復NIO 空輪詢的bug的,但是沒有深入瞭解IO事件在netty中是如何被處理的,下面我們以伺服器端demo程式碼為例子,看下和IO事件處理密切的Channel

如上在編寫netty 伺服器端的時候,我們一般只需要指定Channel型別,以及實現ChannelHandler在對應方法中編寫業務邏輯程式碼即可。

在Netty中,NioEventLoop是事件的排程中心,它控制了Io事件和其他任務的排程,但是io事件的處理是依賴ChannelHandler的,多個ChannelHandler又由ChannelPipline組裝成流水線依次執行

這篇部落格我們以此為切入點,看看Channel是如何初始化的,如何和EventLoop關聯起來的,後續看看ChannelPipline是如何組織ChannelHandler的。

一丶Channel概述

Channel是Netty抽象出來的對網路I/O進行讀寫的相關介面,與JDK NIO中的Channel介面類似。Channel的主要功能有網路I/O的讀寫繫結埠使用者端發起連線主動關閉連線獲取通訊雙方網路地址等。

下面是NioServerSocketChannel,和NioSocketChannel的類圖

隨後會使用SelectorProvider#openServerSocketChannel建立出一個jdk原生的ServerSocketChannel。

然後呼叫父類別構造器,設定Channel為非阻塞,並呼叫newUnsafe和newChannelPipeline範例化unsafe和channelPipeline

對於伺服器端來說這裡newUnsafe產生的是NioMessageUnsafe,ChannelPipeline通常使用的是DefaultChannelPipeline

2.NioServerSocketChannel的初始化

初始化會將我們在ServerBootStrap中設定的引數設定到NioServerSocketChannel中

並向ChannelPipeline新增一個ServerBootstrapAcceptor,ServerBootstrapAcceptor和Netty的reactor模式有關,此類的作用後續進行學習。

3.NioServerSocketChannel的註冊

隨後會使用ServerBootStrap中的EventLoopGroup#register方法進行註冊,這裡的使用的EventLoopGroup是demo中指定的bossGroup

註冊的即將當前Channel註冊到Selector,並且attachment指定為當前Channel,這樣NioEventLoop在進行IO多路複用的的時候,可通過attachment方法拿到當前Channel

註冊結束後會使用ChannelPipeline觸發channelRegistered事件,關於ChannelPipeline下一篇部落格中進行學習。

在這一步,還會觸發NioEventLoop執行緒的啟動,進行事件迴圈,在一個死迴圈中使用Selector監聽這個Channel的IO事件,並處理其他排程任務,非同步任務。(如何啟動NioEventLoop執行緒的——Netty原始碼學習2——NioEventLoop的執行#NioEventLoop的啟動

三丶ChannelPipeline

上面我們說到一個Channel的範例化會觸發ChannelPipeline的範例化。ChannelPipeline 和 ChannelHandler 也是我們在平時應用開發的過程中打交道最多的元件,通常程式設計師使用Netty進行開發只需要將自己定義的ChannelHandler加入到ChannelPipeline中。

ChannelPipeline即是ChannelHandler的流水線,ChannelPipeline 可以看作是 ChannelHandler 的容器載體,它是由一組 ChannelHandler 範例組成的,內部通過雙向連結串列將不同的 ChannelHandler 連結在一起,如下圖所示。當有 I/O 讀寫事件觸發時,ChannelPipeline 會依次呼叫 ChannelHandler 列表對 Channel 的資料進行攔截和處理。

1.ChannelHandlerContext

ChannelHandlerContext 用於儲存 ChannelHandler 上下文,其包含了 ChannelHandler 生命週期的所有事件,如 connect、bind、read、flush、write、close 等。

2.ChannelInboundHandler 和 ChannelOutboundHandler

在使用者端與伺服器端通訊的過程中,資料從使用者端發向伺服器端的過程叫出站,反之稱為入站。資料先由一系列 InboundHandler 處理後入站,然後再由相反方向的 OutboundHandler 處理完成後出站。

3.DefaultChannelPipeline

DefaultChannelPipeline是netty中ChannelPipeline的預設實現,內部儲存了HeadContext,和TailContext分別作為連結串列的頭和尾

可以看到HeadContext即是ChannelOutboundInvoker(出站處理器)也是ChannelInboundInvoker(出站處理器),這是因為網路資料寫入操作的入口就是由 HeadContext 節點完成的。HeadContext 作為 Pipeline 的頭結點負責讀取資料並開始傳遞 入站事件,當資料處理完成後,資料會反方向經過各個 ChannelOutboundInvoker的處理,最終傳遞到 HeadContext。

而TailContext只實現了ChannelInboundInvoker,它是最後一個ChannelInboundInvoker,用於結束入站事件的傳播。

4.ChannelInboundHandler&ChannelOutboundHandler

二者都是ChannelHandler的子介面,其方法的宣告對於了Netty中對事件的抽象

4.1 ChannelInboundHandler

方法名&事件
channelRegistered 當Channel註冊到它的EventLoop並且能夠處理I/O時呼叫
channelUnregistered 當Channel從它的EventLoop中登出並且無法處理任何I/O時呼叫
channelActive 當Channel處理於活動狀態時被呼叫
channelInactive 不再是活動狀態且不再連線它的遠端節點時被呼叫
channelReadComplete 當Channel上的一個讀操作完成時被調
channelRead 當從Channel讀取資料時被呼叫
channelWritabilityChanged 當Channel的可寫狀態發生改變時被呼叫
userEventTriggered 當ChannelInboundHandler.fireUserEventTriggered()方法被呼叫時觸發

4.2 ChannelOutBoundHandler

方法名&事件
bind 當請求將Channel繫結到本地地址時被呼叫
connet 當請求將Channel連線到遠端節點時被呼叫
disconnect 當請求將Channel從遠端節點斷開時呼叫
close 當請求關閉Channel時呼叫
deregister 當請求將Channel從它的EventLoop登出時呼叫
read 當請求從Channel中讀取資料時呼叫
flush 當請求通過Channel將入隊資料沖刷到遠端節點時呼叫
write 當請求通過Channel將資料寫入遠端節點時被呼叫

四丶總結

此篇初探了Channel,ChannelPipeline,ChannelContext,ChannelHandler之間的關係,深入學習了Netty中的Nio Channel是怎麼和jdk中的Channel組織起來的。

上面我們說到在Netty中,NioEventLoop是事件的排程中心,它控制了Io事件和其他任務的排程,但是io事件的處理是依賴ChannelHandler的,多個ChannelHandler又由ChannelPipline組裝成流水線依次執行

那麼一個網路請求在Netty中是怎麼從NioEventLoop事件迴圈中交由ChannelPipline進行事件傳播與處理的暱?這個下篇中進行學習和總結。