成人免费xxxxx在线视频软件_久久精品久久久_亚洲国产精品久久久_天天色天天色_亚洲人成一区_欧美一级欧美三级在线观看

Netty基礎(chǔ)招式——ChannelHandler的優(yōu)秀實踐

開發(fā) 架構(gòu)
今天,我們繼續(xù)學(xué)習(xí)Netty邏輯架構(gòu)中的另一個核心組件ChannelHandler和ChannelPipeline。對ChannelHandler的事件傳播機制、異常處理機制做了詳細(xì)介紹。

[[416157]]

今天,我們繼續(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ù)進(jìn)行加工處理。

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

ChannelPipeline 則是 ChannelHandler 的容器載體,負(fù)責(zé)以鏈?zhǔn)降男问秸{(diào)度各個注冊的ChannelHandler。

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

圖片

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

圖片

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

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

圖片

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

比如,我們經(jīng)常需要對接收到的數(shù)據(jù)進(jìn)行解碼,就是在某一個專門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事件傳遞的末尾,負(fù)責(zé)處理一些資源釋放的工作。在OutBound事件傳遞的第一個節(jié)點,不做任何處理,僅僅傳遞OutBound事件給prev節(jié)點。

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

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

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

2、ChannelHandler的事件傳播機制

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

當(dāng)我們使用Netty進(jìn)行開發(fā)的時候,必須了解Inbound事件和Outbound事件在ChannelPipeline中如何進(jìn)行“事件傳播”,注冊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ù)事件類型進(jìn)行過濾。

3、ChannelHandler的異常傳播機制

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

3.1 InboundHandler的異常處理

我們修改下示例中的TestInboudHandler進(jìn)行模擬。

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

得到輸出如下

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

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

  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中被標(biāo)記為廢棄)

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

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

咦?異常被吃掉了!!

不僅沒有走進(jìn)exceptionCaught方法,也沒有其他異常拋出。

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

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

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

后續(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線程中直接處理耗時操作。
  • 也不把耗時操作放進(jìn)EventLoop的任務(wù)隊列中。

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

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

給個簡單的demo。

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

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

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

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

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

  • 自定義ExceptionHandler繼承ChannelDuplexHandler,并注冊到 tail節(jié)點前(ChannelPipeline的最后一個節(jié)點)。
  • 對于Inbound事件,我們需要在exceptionCaught()進(jìn)行處理。
  • 對于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的事件傳播機制、異常處理機制做了詳細(xì)介紹。

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

希望對大家有所幫助。

 

責(zé)任編輯:姜華 來源: 阿丸筆記
相關(guān)推薦

2021-10-08 09:38:57

NettyChannelHand架構(gòu)

2024-01-11 11:25:22

2019-07-15 10:39:04

云計算基礎(chǔ)設(shè)施監(jiān)控軟件

2019-09-17 09:44:45

DockerHTMLPython

2021-04-15 08:08:48

微前端Web開發(fā)

2019-11-27 10:55:36

云遷移云計算云平臺

2020-11-25 10:26:24

云計算云安全數(shù)據(jù)

2021-01-20 10:53:41

云計算云存儲云遷移

2023-06-29 00:19:51

2023-07-04 15:56:08

DevOps開發(fā)測試

2023-01-13 16:34:08

2022-03-11 18:30:39

DevOps軟件開發(fā)

2019-05-07 09:00:40

無服務(wù)器Lambda管理

2021-12-17 14:06:55

云計算安全工具

2020-05-25 11:14:59

代碼程序開發(fā)

2023-02-07 15:33:16

云遷移數(shù)據(jù)中心云計算

2024-12-12 09:02:35

2020-03-09 14:10:48

代碼開發(fā)工具

2021-08-17 15:00:10

BEC攻擊網(wǎng)絡(luò)攻擊郵件安全

2021-07-06 14:17:16

MLOps機器學(xué)習(xí)AI
點贊
收藏

51CTO技術(shù)棧公眾號

主站蜘蛛池模板: 久久蜜桃av一区二区天堂 | 成人精品鲁一区一区二区 | 伊人网国产 | 久久久久久久国产 | 亚洲视频一区在线播放 | 户外露出一区二区三区 | 日本欧美国产 | 在线成人免费视频 | 午夜私人影院 | 国产精品精品视频一区二区三区 | 精区3d动漫一品二品精区 | 久久精品日产第一区二区三区 | 免费观看a级毛片在线播放 黄网站免费入口 | 亚洲乱码国产乱码精品精98午夜 | 国产黄色在线观看 | 亚洲精品99 | 久久久久国产精品一区二区 | 国产成人免费 | 国产精品免费在线 | 久久久91精品国产一区二区三区 | 日本欧美视频 | 欧美一区免费 | 日韩欧美在线观看视频 | 天堂成人国产精品一区 | 九九热精品视频在线观看 | 欧美成人高清 | 日韩精品免费视频 | h视频免费看 | 欧美激情综合 | 99热精品在线观看 | 欧美视频一级 | 久久国产精品一区二区 | 精品一区二区三区免费毛片 | 国产午夜精品视频 | 亚洲狠狠 | 91在线电影| 亚洲最大成人综合 | 亚洲高清免费观看 | 成人av鲁丝片一区二区小说 | 久久骚| 天天综合国产 |