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

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

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

[[416157]]

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

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

[[416158]]

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

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

所以,阿丸盡可能通過(guò) 圖 和 代碼demo,來(lái)讓大家獲得最直觀的使用體驗(yàn)。

本文預(yù)計(jì)閱讀時(shí)間約 10分鐘,將重點(diǎn)圍繞以下幾個(gè)問(wèn)題展開(kāi):

  • 什么是ChannelHandler和ChannelPipeline?
  • ChannelHandler的事件傳播機(jī)制
  • ChannelHandler的異常處理機(jī)制
  • ChannelHandler的最佳實(shí)踐

1、什么是ChannelHandler和ChannelPipeline

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

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

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

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

圖片

再?gòu)木植糠糯螅梢愿用鞔_地看到ChannelPipeline和ChannelHandler的作用。

圖片

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

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

圖片

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

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

值得一提的是,雖然我們?cè)谑褂肗etty時(shí),直接打交道的是ChannelPipeline和ChannelHandler,但是,它們之間有一座“隱形”的橋梁,名字叫做ChannelHandlerContext。

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

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

圖片

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

圖片

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

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

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

至此,我們對(duì)ChannelHandler和ChannelPipeline有了基本的認(rèn)識(shí)。具體到實(shí)踐上,我們?cè)撊绾握_地使用ChannelHandler呢?

對(duì)ChannelHandler的使用,必須先了解ChannelHandler的事件傳播機(jī)制和異常處理機(jī)制。

2、ChannelHandler的事件傳播機(jī)制

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

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

話不多說(shuō),我們先來(lái)一個(gè)demo直觀地感受一下。

自定義一個(gè)ChannelInboundHandler

圖片

自定義一個(gè)ChannelOutboundHandler

圖片

簡(jiǎn)單組裝一下EchoPipelineServer,特別注意一下 6個(gè)handler 的注冊(cè)順序。

圖片

然后我們通過(guò)命令行簡(jiǎn)單訪問(wèn)一下這個(gè)Netty Server

  1. curl localhost:8081 

可以看到控制臺(tái)的如下輸出

圖片

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

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

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

圖片

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

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

雖然這里是雙向鏈表,但是無(wú)論是Inbound事件還是Outbound事件,在按序訪問(wèn)鏈表節(jié)點(diǎn)時(shí),會(huì)根據(jù)事件類型進(jìn)行過(guò)濾。

3、ChannelHandler的異常傳播機(jī)制

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

3.1 InboundHandler的異常處理

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

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

得到輸出如下

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

我們也看到了,Netty給出了會(huì)警告,在最后的節(jié)點(diǎn)沒(méi)有進(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也是這么操作嗎?

我們來(lái)做個(gè)實(shí)驗(yàn)。

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

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

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

咦?異常被吃掉了!!

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

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

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

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

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

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

上代碼:

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

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

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

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

異常就這樣抓到了。

4、ChannelHandler的最佳實(shí)踐

其實(shí)前面已經(jīng)對(duì)ChannelHandler的常用機(jī)制做了介紹,這里簡(jiǎn)單再介紹下兩個(gè)最佳實(shí)踐。

4.1 不在ChannelHandler中耗時(shí)處理

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

這里包括兩點(diǎn)。

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

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

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

給個(gè)簡(jiǎn)單的demo。

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

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

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

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

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

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

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

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

5、小結(jié)

來(lái)簡(jiǎn)單回顧下吧。

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

然后對(duì)ChannelHandler的事件傳播機(jī)制、異常處理機(jī)制做了詳細(xì)介紹。

最后說(shuō)明了日常開(kāi)發(fā)中ChannelHandler的最佳實(shí)踐。

希望對(duì)大家有所幫助。

 

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

2021-10-08 09:38:57

NettyChannelHand架構(gòu)

2024-01-11 11:25:22

2019-07-15 10:39:04

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

2019-09-17 09:44:45

DockerHTMLPython

2021-04-15 08:08:48

微前端Web開(kāi)發(fā)

2019-11-27 10:55:36

云遷移云計(jì)算云平臺(tái)

2020-03-09 14:10:48

代碼開(kāi)發(fā)工具

2021-08-17 15:00:10

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

2021-07-06 14:17:16

MLOps機(jī)器學(xué)習(xí)AI

2022-12-21 08:20:01

2020-11-25 10:26:24

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

2021-01-20 10:53:41

云計(jì)算云存儲(chǔ)云遷移

2023-06-29 00:19:51

2023-07-04 15:56:08

DevOps開(kāi)發(fā)測(cè)試

2022-03-11 18:30:39

DevOps軟件開(kāi)發(fā)

2019-05-07 09:00:40

無(wú)服務(wù)器Lambda管理

2021-12-17 14:06:55

云計(jì)算安全工具

2023-01-13 16:34:08

2020-05-25 11:14:59

代碼程序開(kāi)發(fā)

2023-02-07 15:33:16

云遷移數(shù)據(jù)中心云計(jì)算
點(diǎn)贊
收藏

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

主站蜘蛛池模板: 一区二区在线免费观看 | 国产视频一区二区 | 亚洲最大的黄色网址 | 国产视频中文字幕 | 精品二区 | 日韩不卡一区二区 | 一区二区精品 | 精品一区二区三区在线观看 | 蜜桃视频在线观看免费视频网站www | 国产精品久久毛片av大全日韩 | 男人天堂免费在线 | 久久久成人网 | 激情的网站 | 99国产精品99久久久久久 | 国产精品视频网 | 国产中文字幕在线 | 日韩精品一区二区三区中文在线 | 99久久久无码国产精品 | 久久成人一区 | 狠狠做六月爱婷婷综合aⅴ 国产精品视频网 | 激情毛片| 午夜a区 | 欧美亚洲另类丝袜综合网动图 | 国产丝袜一区二区三区免费视频 | 欧美jizzhd精品欧美巨大免费 | 欧美天堂 | 午夜视频免费网站 | 久草在线中文888 | 精品1区2区 | 国产中文字幕网 | 国产精品美女在线观看 | 大学生a级毛片免费视频 | 欧美 日韩 国产 成人 在线 | 精品久久精品 | 看av电影 | 91精品国产91久久久久青草 | 免费能直接在线观看黄的视频 | 中文字幕第一页在线 | 亚洲午夜在线 | 免费看a| 男人的天堂久久 |