Netty快速入門(09)channel組件介紹

書接上回,繼續介紹組件。

ChannelHandler組件介紹


ChannelHandler組件包含了業務處理核心邏輯,是由用戶自定義的內容,開發人員百分之九十的代碼都是ChannelHandler。Netty提供2個重要的 ChannelHandler 子接口,用來自定義ChannelHandler:

ChannelInboundHandler - 處理進站數據和所有狀態更改事件(進站指的是讀操作等由通道引發的事件)

ChannelOutboundHandler - 處理出站數據,允許攔截各種操作(出站指的是寫操作等由用戶觸發的事件,發送到遠方服務器的事件)

來看一下ChannelHandler的類層次結構:

Netty快速入門(09)channel組件介紹

上面的類中的Adapter類,提供很多默認操作,比如ChannelHandler中有很多很多方法,我們用戶自定義的方法有時候不需要重載全部,只需要重載一兩個方法,那麼可以使用Adapter類,它裡面有很多默認的方法。其它框架中結尾是Adapter的類的作用也大都是如此。所以我們在使用netty的時候,往往很少直接實現ChannelHandler的接口,經常是繼承Adapter類。


Channel生命週期


我們寫入門例子的時候,重載了幾個方法,這幾個屬於生命週期的一部分。我們之所以瞭解channel的生命週期,是因為生命週期的每個階段都會綁定一個事件,我們可以捕獲這個事件進行處理,Handler類裡面主要的內容就是處理這些事件。我們來看一下channel的狀態:

Netty快速入門(09)channel組件介紹

下面是狀態之間的轉換:

Netty快速入門(09)channel組件介紹

上面是channel四種類型的狀態和事件,我們來回顧入門示例中,客戶端在處於active狀態,也就是連接到遠程服務器的時候,我們做了一件事,就是發送一個消息:

Netty快速入門(09)channel組件介紹

也就是channel在進行狀態轉換的時候,進站出站的類可以監聽到,我們的handler繼承了這些類以後,可以處理這些事件,重載方法。

當 ChannelHandler 添加到 ChannelPipeline,或者從ChannelPipeline 移除後,也會有一些狀態的轉換,對應的事件也會被監聽到,對應的方法將會被調用:

Netty快速入門(09)channel組件介紹

ChannelInboundHandler主要處理入站事件,入站的時候發生的事件有很多,當接收到數據或者與之關聯的Channel 狀態改變時調用。ChannelInboundHandler的生命週期與 Channel 的生命週期接近,中間有很多和業務相關的事件:

Netty快速入門(09)channel組件介紹

從這裡我們就看到了很多熟悉的方法,在我們的入門例子中,處理過讀事件。在這裡如果相應的事件發生的時候有業務需要進行處理,我們在寫handler的時候重載相應的方法即可。同理,ChannelOutboundHandler中也有很多方法和事件:

Netty快速入門(09)channel組件介紹


ChannelPipeline組件


瞭解了ChannelInboundHandler和ChannelOutboundHandler,我們來看看ChannelPipeline。ChannelPipeline其實就是一個ChannelHandler容器,裡面包括一系列的ChannelHandler 實例,用於攔截流經一個Channel 的入站和出站事件,每個Channel都有一個ChannelPipeline,我們要給channel添加處理類,可以修改 ChannelPipeline 通過動態添加和刪除 ChannelHandler,它定義了豐富的API調用來回應入站和出站事件。

Netty快速入門(09)channel組件介紹

我們自己定義的handler也都會添加進去:

Netty快速入門(09)channel組件介紹

這裡面可以調用上面的三個方法操作多個。


ChannelHandlerContext組件


有一個問題,建立連接後,handler的參數如何傳遞?就是靠ChannelHandlerContext來傳遞的。ChannelHandlerContext表示ChannelHandler 和ChannelPipeline 之間的關聯,在ChannelHandler 添加到 ChannelPipeline 時創建ChannelHandlerContext表示兩者之間的關係,

Netty快速入門(09)channel組件介紹

由於每個ChannelHandler都對應一個ChannelHandlerContext,所以ChannelHandler之間其實沒有聯繫,都是由ChannelHandlerContext關聯起來的。在我們的入門例子中,處理讀事件的時候,有印象的一定還記得裡面就是通過ChannelHandlerContext獲取參數的:

Netty快速入門(09)channel組件介紹


流程總結


介紹完上面的大致可以總結流程了,服務端有EventLoopGroup,每個EventLoopGroup有很多EventLoop,每個EventLoop裡面有Selector,Selector裡面註冊了很多channel,我們的netty服務端接收的一個個連接就是一個個channel,每個channel都有一個ChannelPipeline,而ChannelPipeline就是ChannelHandler的容器,裡面存放了很多個ChannelHandler實例。ChannelHandler實例就是我們處理一個個業務的類。我們一個channel可能有很多ChannelHandler,如何串起來呢?其實多個ChannelHandler直間沒有直接關係,每個ChannelHandler都對應一個ChannelHandlerContext,ChannelHandlerContext之間是有連接關係的,多個ChannelHandler就是靠他們各自對應的ChannelHandlerContext串聯起來的。來看一個流程圖:

Netty快速入門(09)channel組件介紹

ChannelHandlerContext為什麼是雙向的呢?其實ChannelPipeline中包含進站和出站操作,都是放在一個鏈表裡面的,進和出的方向肯定不同,所以ChannelHandlerContext之間是雙向的,比如如果是Inbound操作,那麼在整個鏈表中我只看InBoundHandler,遇到OutBoundHandler就會跳過,所以說,Inbound和OutBound會放在一個鏈中,不是兩條鏈。


程序示例


我們再來看一個程序例子,一樣的流程,先看服務端:

Netty快速入門(09)channel組件介紹

這裡面的代碼基本上和入門例子基本一樣,只是channel多個兩個配置,一個是128個阻塞,一個是keepalived。我們看一下channelpipline的配置:

Netty快速入門(09)channel組件介紹

最下面的ServerHandler還是我們的業務處理類,上面的StringDecoder和StringEncoder可以看出是編碼和解碼的協議,最上面的DelimiterBasedFrameDecoder是一個解碼器,這個解碼器後面的參數是 (8192, Delimiters.lineDelimiter()) ,就是按行解碼,有tcp的數據包來了,比如在消息中有n換行的符號,那就算是一個包,如果到了8192大小的數據還沒有,就把包丟棄,比如:

this isn a netty worldn

就是兩個包。

我們來看ServerHandler裡面的處理,

Netty快速入門(09)channel組件介紹

這個處理也很簡單,channelRead方法裡面就是打印出來,然後回寫回去,末尾加了一個換行符。ctx.channel().remoteAddress()表示遠程連接的地址。channelReadComplete方法表示read完後,打印服務端讀取完成,flush就是發送到網絡上。

客戶端的代碼基本上和入門例子一樣:

Netty快速入門(09)channel組件介紹

這裡面的不同是在channel的pipline裡面也多加了三個處理器,內容基本上和服務端一樣,客戶端handler代碼:

Netty快速入門(09)channel組件介紹

客戶端的handler中多重載的幾個方法,大家可以對應一下前面講的生命週期中的狀態,channelRegistered方法中是註冊成功後的打印一條信息,channelActive方法是channel激活並連接到遠程後,打印信息併發送一個hello,channelRead方法是客戶端讀取服務端返回的信息後,打印在控制檯上,然後發送給服務端,channelReadComplete方法表示讀取完畢後發送和打印,exceptionCaught方法表示發生異常時的操作,我們來啟動服務端和客戶端看一下效果:

Netty快速入門(09)channel組件介紹

上面發送的第一個信息hello後面跟了一個n,因為服務端和客戶端都是接到消息後返回給另一端,導致無限循環互相發送的效果,如果把hello字符串的換行符去掉,看一下效果:

Netty快速入門(09)channel組件介紹

可以看到沒有打印了,這是因為程序中的第一個handler是根據換行符分隔數據包的,這裡的消息沒有換行符,所以程序認為消息沒有結束,這時候就會卡在第一個handler裡面,不會走到下面的字符串協議中,更不會走到ServerHandler中。這也是一種網絡編程中的半包問題,也就是包沒有結束,不完整。


pipline中的handler是按照加入的順序處理的,我們執行的是addLast方法:

Netty快速入門(09)channel組件介紹

這個就決定了幾個handler的順序。


代碼地址:https://gitee.com/blueses/netty-demo 07

本文由博客一文多發平臺 https://openwrite.cn?from=article_bottom 發佈!


分享到:


相關文章: