Netty學習之ChannelHandler&ChannelPipeline
Netty學習之ChannelHandler&ChannelPipeline
前言
ChannelHandler
Channel生命週期
- ChannelUnregistered,Channel被建立,但是還沒有註冊到EventLoop中
- ChannelRegisted,Channel註冊到EventLoop中
- ChannelActive,Channel啟用(連線到遠端埠),能夠傳送以及接受訊息
- ChannelInActive,Channel沒有連線到遠端埠
生命週期流程
ChannelRegisted --> ChannelActive --> ChannelInactive --> ChannelUnregistered
ChannelHandler生命週期
- handlerAdded,當ChannelHandler新增到ChannelPipeline時呼叫
- handlerRemoved,當ChannelHandler從ChannelPipline中移除時呼叫
- exceptionCaught,在ChannelPipline處理時發生異常時呼叫
注意上面每個方法都接收一個ChannelHandlerContext
作為引數
ChannelHandler有重要的兩個子介面
- ChannelInboundHandler,處理輸入的資料並且處理各種狀態轉變
- ChannelOutboundHandler,處理輸出的資料並且攔截各種操作
ChannelInboundHandler生命週期
- channelRegistered,當Channel註冊到對應的EventLoop並且能夠處理I/O時呼叫
- channelUnregistered,當channel從EventLoop中移除並且不能處理任何I/O時呼叫
- channelActive,當channel啟用時呼叫,此時channel已經連線並且準備好
- channelInactive,當通道斷開連線時呼叫
- channelReadComplete,讀操作在Channel中已經完成時呼叫(讀取完成)
- channelRead,資料從channel中讀取時呼叫(開始讀取)
- channelWritabilityChanged,寫狀態改變時呼叫
- userEventTriggered,當因為管線中傳遞物件時ChannelInboundHanlder.fireUserEventTriggered()被呼叫時呼叫
當覆蓋ChannelInboundHandler的channelRead方法時,需要手動釋放對應的記憶體,可以通過ReferenceCountUtil.release(msg)
方法進行釋放操作
更簡單的方式是通過SimpleChannelInboundHandler
,然後覆蓋其channelRead0()
方法,該方法會自動釋放資源,所以,不能儲存稍後會使用到的資料,因為這些資料都會失效,通過原始碼就可以發現,該方法被channelRead()
呼叫,並且channelRead0()
方法被呼叫後,會釋放所佔用記憶體。
public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {
boolean release = true;
try {
if (acceptInboundMessage(msg)) {
@SuppressWarnings("unchecked")
I imsg = (I) msg;
// 這裡呼叫channelRead0
channelRead0(ctx, imsg);
} else {
release = false;
ctx.fireChannelRead(msg);
}
} finally {
if (autoRelease && release) {
// 釋放空間
ReferenceCountUtil.release(msg);
}
}
}
ChannelOutboundHandler
ChannelOutboundHandler的方法由Channel、ChannelPipeline、ChannelHandlerContext呼叫
主要的操作方法有write()
、flush()
大部分的方法都會帶一個ChannelPromise
引數,當操作完成時可以收到通知,該介面是ChannelFuture
的子介面。
在Netty中,同時還提供了ChannelInboundHandlerAdapter
、ChannelOutboundHanlderAdapter
類,一般只需要繼承這兩個類,然後覆蓋想要實現邏輯的方法即可。
資源管理
在使用ChannelInboundHanlder#channelRead()
或者ChannelOutboundHandler#write()
時,需要保證沒有資源洩露,Netty使用引用技術來處理快取的ByteBuf,所以使用完ByteBuf之後更改引用是非常重要的。
Netty提供了一個ResouceLeakDetecor工具類用於檢測是否發生記憶體洩露,如果發生洩露,則會列印對應的日誌資訊。有四個不同的級別
-
DISABLED
,關閉洩露檢查 -
SIMPLE
,預設級別,樣本率為1% -
ADVANCED
,報告記憶體洩露以及對應的資訊 -
PARANOID
,每個類均取樣,會嚴重影響效能
如果一個訊息已經被消費或者被丟棄,並且不再在管道中傳遞給下一個接受者,則需要手動呼叫ReferenceCountUtil.release(msg)
將其釋放,如果訊息到達傳輸層,當訊息被寫出或者通道關閉後,會自動進行釋放。
ChannelPipeline
每個新的Channel都會繫結一個新的ChannelPipeline,並且是永久性的,Channel不能繫結新的ChannelPipeline,也不能從當前Channel中解綁(開發者無需關心繫結以及解綁)
ChannelHandlerContext使得一個ChannelHanlder能夠與ChannelPipeline以及其他的ChannelHandler進行通訊
由於ChannelPipeline傳播事件,它會檢測在管線中的下一個ChannelHandler是否匹配移動的方法,如果不匹配,則跳過該ChannleHandler,並且處理下一個,直到找到一個匹配的ChannelHandler。
ChannelPipeline中提供了眾多的方法用於操作ChannelHandler,如addFirst
、addLast
、remove
等。同時,還提供了眾多的方法用於呼叫下一個ChannelInboundHandler方法的方法,如fireChannelRead()
、fireExceptionCaught()
等,呼叫下一個ChannelOutboundHandler方法的方法,如flush()
、write()
、writeAndFlush()
等。
ChannelHandlerContext
ChannelHandlerContext表示ChannelHandler和ChannelPipeline之間的連線,當一個ChannelHandler被新增到ChannelPipeline時會被建立。
ChannelPipeline的主要作用是管理其所關聯的ChannelHandler與其他在ChannelPipeline中的ChannelHandler的互動
如果呼叫Channel或者ChannelPipeline中的某些方法,對應的操作會在整個ChannelPipeline中傳輸,而通過ChannelHandlerContext呼叫,只傳輸給下一個能夠處理該事件的ChannelHandler
異常處理
如果是ChannelInboundHandler中出現異常,會逐步進行傳遞,所以,一般在最後一個channelHandler中覆蓋caughtException()
,實現對應的異常處理邏輯
如果是在ChannelOutboundHandler中出現異常,可以有一下機制
- 每一個outbound操作會返回一個ChannelFuture,註冊了的ChannelFutureListener會收到對應的訊息,即成功或者失敗
- 幾乎所有的channelOutboundHanlder會傳遞一個ChannelPromise例項,該例項是ChannelFuture的子類,ChannelPromise能夠註冊監聽器用於非同步通知,也可以直接通知操作,
setSuccess()
、setFailure()
總結
本小節我們主要詳細學習了ChannelHandler以及ChannelPipeline,ChannelHandler是Netty應用中邏輯的存放地方,基本上我們主要互動的物件也是ChannelHandler,不同的ChannelHandler之間通過ChannelPipeline連線起來,構成一個Handler處理鏈,而ChannelHandler與ChannelPipeline之間則是通過ChannelHandlerContext進行連線,不同的物件用途不同,使用的時候需要注意。
相關文章
- Netty學習筆記之ChannelHandlerNetty筆記
- netty學習(三)springboot+netty+mybatisNettySpring BootMyBatis
- Netty 框架學習 —— Netty 元件與設計Netty框架元件
- Netty 框架學習 —— 第一個 Netty 應用Netty框架
- Netty 框架學習 —— 引導Netty框架
- Netty 框架學習 —— 傳輸Netty框架
- 深入學習Netty(4)——Netty程式設計入門Netty程式設計
- Netty原始碼學習系列之5-NioEventLoop的run方法Netty原始碼OOP
- Netty原始碼學習系列之4-ServerBootstrap的bind方法Netty原始碼Serverboot
- 為什麼要學習Netty?Netty
- Netty 框架學習 —— ChannelHandler 與 ChannelPipelineNetty框架
- Netty 框架學習 —— UDP 廣播Netty框架UDP
- Netty 框架學習 —— 新增 WebSocket 支援Netty框架Web
- Netty原始碼學習系列之2-ServerBootstrap的初始化Netty原始碼Serverboot
- Netty原始碼學習系列之1-NioEventLoopGroup的初始化Netty原始碼OOP
- netty系列之:netty初探Netty
- Netty 框架學習 —— 基於 Netty 的 HTTP/HTTPS 應用程式Netty框架HTTP
- 慕課網《Netty入門之WebSocket初體驗》學習總結NettyWeb
- 《Netty實戰》-學習筆記1Netty筆記
- Netty 框架學習 —— 單元測試Netty框架
- Dubbo原始碼學習之-通過原始碼看看dubbo對netty的使用原始碼Netty
- netty系列之:netty架構概述Netty架構
- Netty學習筆記(五)NioEventLoop啟動Netty筆記OOP
- 深入學習Netty(一)NIO基礎篇Netty
- Netty 框架學習 —— 編解碼器框架Netty框架
- Netty之ChannelOptionNetty
- netty系列之:NIO和netty詳解Netty
- netty系列之:netty對marshalling的支援Netty
- Java IO學習筆記八:Netty入門Java筆記Netty
- Netty 框架學習 —— EventLoop 和執行緒模型Netty框架OOP執行緒模型
- netty系列之:netty中的Channel詳解Netty
- netty系列之:netty中的ByteBuf詳解Netty
- netty系列之:在netty中使用proxy protocolNettyProtocol
- netty系列之:在netty中處理CORSNettyCORS
- 深入學習Netty(2)——傳統NIO程式設計Netty程式設計
- 深入學習Netty(3)——傳統AIO程式設計NettyAI程式設計
- 深入學習Netty(5)——Netty是如何解決TCP粘包/拆包問題的?NettyTCP
- 打算寫一些Netty的文章了,先聊聊為什麼要學習NettyNetty