91在线一级黄片|91视频在线观看18|成人夜间呦呦网站|91资源欧美日韩超碰|久久最新免费精品视频一区二区三区|国产探花视频在线观看|黄片真人免费三级片毛片|国产人无码视频在线|精品成人影视无码三区|久久视频爱久久免费精品

RELATEED CONSULTING
相關(guān)咨詢
選擇下列產(chǎn)品馬上在線溝通
服務(wù)時間:8:30-17:00
你可能遇到了下面的問題
關(guān)閉右側(cè)工具欄

新聞中心

這里有您想知道的互聯(lián)網(wǎng)營銷解決方案
Netty基礎(chǔ)招式——ChannelHandler的優(yōu)秀實踐

今天,我們繼續(xù)學(xué)習(xí)Netty邏輯架構(gòu)中的另一個核心組件ChannelHandler和ChannelPipeline。

如果說線程模型是Netty的 “核心內(nèi)功”,那么ChannelHandler就是Netty最著名的 “武功招式”,是我們?nèi)粘J褂肗etty時接觸最多的組件。

[[416158]]

引用《Netty in action》中的一句話

  • From the appliaction developer's standpoint, the primary component of Netty is the ChannelHandler.

所以,阿丸盡可能通過 圖 和 代碼demo,來讓大家獲得最直觀的使用體驗。

本文預(yù)計閱讀時間約 10分鐘,將重點圍繞以下幾個問題展開:

  • 什么是ChannelHandler和ChannelPipeline?
  • ChannelHandler的事件傳播機制
  • ChannelHandler的異常處理機制
  • ChannelHandler的最佳實踐

1、什么是ChannelHandler和ChannelPipeline

ChannelHandler是一個包含所有應(yīng)用處理邏輯的容器載體,用來對Netty的輸入輸出數(shù)據(jù)進行加工處理。

比如數(shù)據(jù)格式轉(zhuǎn)換、異常處理等

ChannelPipeline 則是 ChannelHandler 的容器載體,負責(zé)以鏈式的形式調(diào)度各個注冊的ChannelHandler。

我們回顧下之前介紹過的Netty邏輯架構(gòu),觀察下ChannelPipeline和ChannelHandler的位置。

再從局部放大,可以更加明確地看到ChannelPipeline和ChannelHandler的作用。

如上圖所示,當(dāng)EventLoop中監(jiān)聽到事件后,會對I/O事件進行處理。而這個處理,就是交給ChannelPipeline進行,更嚴格地說,是交給ChannelPipeline中的各個ChannelHandler按照一定的順序進行處理。

根據(jù)數(shù)據(jù)的流向,Netty把ChannelHandler分為2類,InboundHandler和OutboundHandler。

如上圖所示,Netty接收到數(shù)據(jù)后,經(jīng)過若干 InboundHandler 處理后接收成功。如果要輸出數(shù)據(jù),就需要經(jīng)過若干個 OutboundHandler 處理完成后發(fā)送。

比如,我們經(jīng)常需要對接收到的數(shù)據(jù)進行解碼,就是在某一個專門decode的InboundHandler中處理的。如果要發(fā)送數(shù)據(jù),往往需要編碼,就是在某一個專門encode的OutBoundHandler中處理的。

值得一提的是,雖然我們在使用Netty時,直接打交道的是ChannelPipeline和ChannelHandler,但是,它們之間有一座“隱形”的橋梁,名字叫做ChannelHandlerContext。

顧名思義,ChannelHanderContext就是ChannelHandler的上下文,每個 ChannelHandler 都對應(yīng)一個 ChannelHandlerContext。

每一個 ChannelPipeline 都包含多個 ChannelHandlerContext,所有 ChannelHandlerContext 之間組成了雙向鏈表。如下圖所示。

其中,有兩個特殊的ChannelHandlerContext,分別是HeadContext和TailContext,表示雙向鏈表的頭尾節(jié)點。

從類圖上可以看到,HeadContext同時實現(xiàn)了ChannelInboundHandler和ChannelOutboundHandler。因此,HeadContext在讀取數(shù)據(jù)時作為頭節(jié)點,向后傳遞InBound事件,同時,在寫數(shù)據(jù)時作為尾節(jié)點,處理最后的OutBound事件。

TailContext只實現(xiàn)了ChannelInboundHandler。它在InBound事件傳遞的末尾,負責(zé)處理一些資源釋放的工作。在OutBound事件傳遞的第一個節(jié)點,不做任何處理,僅僅傳遞OutBound事件給prev節(jié)點。

而我們平時自定義的ChannelHandler,就是插在這兩個頭尾節(jié)點之間的。

至此,我們對ChannelHandler和ChannelPipeline有了基本的認識。具體到實踐上,我們該如何正確地使用ChannelHandler呢?

對ChannelHandler的使用,必須先了解ChannelHandler的事件傳播機制和異常處理機制。

2、ChannelHandler的事件傳播機制

前面我們提到了Netty中的兩種事件類型,Inbound事件和Outbound事件,分別對應(yīng)InboundHandler和OutbountHandler進行處理。

當(dāng)我們使用Netty進行開發(fā)的時候,必須了解Inbound事件和Outbound事件在ChannelPipeline中如何進行“事件傳播”,注冊InboundHandler和OutboundHandler的順序有什么影響。

話不多說,我們先來一個demo直觀地感受一下。

自定義一個ChannelInboundHandler

自定義一個ChannelOutboundHandler

簡單組裝一下EchoPipelineServer,特別注意一下 6個handler 的注冊順序。

然后我們通過命令行簡單訪問一下這個Netty Server

 
 
 
 
  1. curl localhost:8081 

可以看到控制臺的如下輸出

這樣就清楚了事件傳播順序:

  • - 對于Inbound事件,InboundHandler的處理順序是和注冊順序一致
  • - 對于Outbound事件,OutboundHandler的處理順序和注冊順序相反

結(jié)合上一節(jié)說的HeadContext和TailContext,我們畫個圖來更直觀地看一下這個ChannelPipeline中的handler構(gòu)建順序是怎樣的。

在上面的ChannelInitializer中,我們按需添加了3個InboundHandler和3個OutboundHandler。所以,在頭節(jié)點HeadContext和TailContext之間,有序構(gòu)成了雙向鏈表。

而InboundHandler3中,通過調(diào)用 ctx.channel.writeAndFlush( msg ) 方法,將消息從TailContext開始,依據(jù)OutboundHandler的路徑向HeadContext方向傳播出去。具體可以看下DefaultChannelPipeline類中的實現(xiàn)

雖然這里是雙向鏈表,但是無論是Inbound事件還是Outbound事件,在按序訪問鏈表節(jié)點時,會根據(jù)事件類型進行過濾。

3、ChannelHandler的異常傳播機制

我們已經(jīng)了解了ChannelPipeline的鏈式傳遞規(guī)則,如果雙向鏈表中任意一個handler拋出了異常,那么應(yīng)該怎么處理呢?

3.1 InboundHandler的異常處理

我們修改下示例中的TestInboudHandler進行模擬。

  • channelRead方法中拋出異常
  • 重寫exceptionCaught方法,打印當(dāng)前節(jié)點捕獲異常情況

得到輸出如下

可以看到,雖然在InboundHander1中拋出了異常,但是仍然會被3個InboundHandler都捕獲一次,并按序向tail節(jié)點方向傳遞,然后拋出異常。

我們也看到了,Netty給出了會警告,在最后的節(jié)點沒有進行異常處理。

 
 
 
 
  1. An exceptionCaught() event was fired, and it reached at the tail of the pipeline.  
  2. It usually means the last handler in the pipeline did not handle the exception. 

3.2 OutboundHandler的異常處理

OutboundHandler也是這么操作嗎?

我們來做個實驗。

  • 在write操作中拋出異常
  • 重寫下exceptionCaught方法(這個方法在OutboundHandler中被標記為廢棄)

重寫組裝下channelPipeline,第二個OutboundHandler中拋出異常

結(jié)果得到的輸出如下:

咦?異常被吃掉了!!

不僅沒有走進exceptionCaught方法,也沒有其他異常拋出。

只是對后續(xù)handler的write方法不再執(zhí)行,而flush方法還是都執(zhí)行了一遍。

我們從源碼找找原因吧。跟一下斷點,馬上就找到了原因:

在AbstractChannelHandlerContext中,對OutboundHandler的write方法做了異常捕獲,然后對ChannelPromise進行了通知。

后續(xù)源碼就不展開了,有興趣的同學(xué)自己打斷點跟一下,比較清楚。

那么問題來了,怎么在OutboundHandler中捕獲異常呢?很明顯就是直接添加ChannelPromise的回調(diào)。

上代碼:

在前面提到的ExceptionHandler中,復(fù)寫write方法,然后注冊一個ChannelPromise的Listener就行了。

當(dāng)然,這個ExceptionHandler同樣要注冊到ChannelPipeline。

千萬注意!!這里ExceptionHandler同樣是添加到ChannelPipeline的tail方向的最后,而不是添加在head方向。

無論是inboundHandler或者是outboundHandler的異常,都是按序向tail方向傳遞的。

異常就這樣抓到了。

4、ChannelHandler的最佳實踐

其實前面已經(jīng)對ChannelHandler的常用機制做了介紹,這里簡單再介紹下兩個最佳實踐。

4.1 不在ChannelHandler中耗時處理

這一點其實在前一篇《 深入Netty邏輯架構(gòu),從Reactor線程模型開始》已經(jīng)提到過,這里作為自定義ChannelHandler的最佳實踐再強調(diào)一下,不在ChannelHandler中做耗時處理。

這里包括兩點。

  • 不在I/O線程中直接處理耗時操作。
  • 也不把耗時操作放進EventLoop的任務(wù)隊列中。

由于Netty4的無鎖串行化設(shè)計,一旦任何耗時操作阻塞了某個EventLoop,那么這個EventLoop上的各個channel都會被阻塞。更詳細內(nèi)容可以參考上一篇《 深入Netty邏輯架構(gòu),從Reactor線程模型開始》。

所以,我們對于耗時操作,我們要放在自己的業(yè)務(wù)線程池中進行處理,如果需要發(fā)送response,需要提交任務(wù)到EventLoop的任務(wù)隊列中執(zhí)行。

給個簡單的demo。

4.2 統(tǒng)一的異常處理

在本文的第三節(jié)中,講解了ChannelHandler的異常傳播機制。

對于InboundHandler來說,如果你有跟handler特定相關(guān)的異常,可以直接在handler里進行exceptionCaught。如果是一些通用的異常,可以自定義ExceptionHandler注冊到ChannelPipeline的末尾進行統(tǒng)一攔截。

對于OutboudHandler來說,就是通過自定義ExceptionHandler,重寫對應(yīng)方法,并注冊ChannelPromise的Listener。同樣的,ExceptionHandler注冊到ChannelPipeline的末尾進行統(tǒng)一攔截。

所以,總結(jié)下如何添加一個“統(tǒng)一”的異常攔截器呢?

  • 自定義ExceptionHandler繼承ChannelDuplexHandler,并注冊到 tail節(jié)點前(ChannelPipeline的最后一個節(jié)點)。
  • 對于Inbound事件,我們需要在exceptionCaught()進行處理。
  • 對于Outbound事件,我們需要對OutboundHandler的不同方法(如write、flush)注冊ChannelFutureListener事件。

異常攔截器的注冊位置應(yīng)該在tail方向的最后一個Handler。

注意,統(tǒng)一異常處理除了更優(yōu)雅處理通用異常外,也是排查故障的好幫手。比如有時候?qū)τ诰幗獯a異常,可以在統(tǒng)一處理異常處捕獲,快速定位問題。

5、小結(jié)

來簡單回顧下吧。

本文介紹了什么是ChannelHandler和ChannelPipeline。能厘清InboundChannelHandler、OutboundChannelHandler、ChannelHandlerContext是什么嗎?

然后對ChannelHandler的事件傳播機制、異常處理機制做了詳細介紹。

最后說明了日常開發(fā)中ChannelHandler的最佳實踐。

希望對大家有所幫助。


當(dāng)前文章:Netty基礎(chǔ)招式——ChannelHandler的優(yōu)秀實踐
路徑分享:http://www.jiaoqi3.com/article/cdhgcgg.html