Netty-ChannelHandler-ChannelPipeline

ChannelHandler

Channel生命週期

狀態 描述
ChannelUnregistered Channel已經被建立,但未註冊到EventLoop
ChannelRegistered Channel已經被註冊到了EventLoop
ChannelActive Channel處於活動狀態(已經鏈接到它的遠程節點)。如今Channel能夠接受和發送數據
ChannelInActive Channel沒有鏈接到遠程節點

通常Channel的生命週期順序ChannelRegistered -> ChannelActive -> ChannelInactive -> ChannelUnregistered。java

當Channel的狀態發生變化時,將會生成對應的事件。與此同時,這些事件會被轉發給ChannelPipeline中的ChannelHandler。緩存

ChannelHandler生命週期

ChannelHandler定義的生命週期操做,在ChannelHandler被添加到ChannelPipeline中或者被從ChannelPipeline中移除時會調用這些方法。這些方法中均可以接受一個ChannelHandlerContext參數安全

類型 描述
handlerAdded 當把ChannelHandler添加到ChannelPipeline中時被調用
handlerRemoved 當從ChannelHandler在ChannelPipeline移除時調用
exceptionCaught 當處理過程當中在ChannelPipeline中有錯誤產生時被調用

Netty中定義了下面兩個重要的ChannelHandler接口:併發

  1. ChannelInboundHandler——處理入站數據以及各類狀態變化
  2. CHannelOutboundHandler——處理出站數據而且容許攔截全部的操做

ChanneInboundHandler接口

類型 描述
channelRegistered 當Channel已經註冊到它的EventLoop而且可以處理I/O時被調用
channelUnregistered 當Channel從它的EventLoop註銷而且沒法處理任何I/O時被調用
channelActive 當Channel處於活動狀態時被調用;Channel已經鏈接/綁定而且已經就緒
channelInactive 當Channel離開活動狀態而且再也不鏈接它的遠程節點時被調用
channelReadComplete 當Channel的一個讀操做完成時被調用
channelRead 當從Channel讀取數據時被調用
channelWritabilityChanged 當Channel的可寫狀態發生改變時被調用。用戶能夠確保寫操做不會完成的太快(以免發生OutOfMemoryError)或者能夠在Channel變爲再次可寫時恢復寫入。Channel的isWriteable()方法能夠來檢測Channel的可寫性。與可寫性相關的閥值能夠經過Channel.config().setWriteHighWaterMark()和Channel.config().setWriteLowWaterMark()方法來設置
userEventTriggered 當ChannelInboundHandler.fireUserEventTriggered()方法被調用時被調用。

當某個ChannelInboundHandler的實現重寫channelRead()方法時,它將負責顯示地釋放與池化的ByteBuf實例相關的內存。ReferenceCountUtil.release()異步

Netty會使用WARN級別的日誌消息記錄未釋放的資源,可是以這種方式管理資源可能很是繁瑣,Netty採用SimpleChannelInboundHandler來簡化這種操做。oop

ChannelOutboundHandler接口

出站操做和數據將由ChannelOutboundHandler處理。它的方法將被Channel、ChannelPipeline以及ChannelHandlerContext調用。佈局

ChannelOutboundHandler能夠按需推遲操做或者事件。性能

類型 描述
bind(ChannelHandlerContext, SockertAddress, ChannelPromise) 當請求將Channel綁定到本地地址時被調用
connect(ChannelHandlerContext, SocketAddress, SockertAddress, ChannelPromise) 當請求將Channel鏈接到遠程節點時被調用
disconnect(ChannelHandlerContext, ChannelPromise) 當請求將Channel從遠程節點斷開時被調用
close(ChannelHandlerContext, ChannelPromise) 當請求關閉Channel時被調用
deregister(ChannelHandlerContext, ChannelPromise) 當請求將Channel叢它的EventLoop註銷時被調用
read(ChannelHandlerContext) 當請求從Channel讀取更多的數據時被調用
flush(ChannelHandlerContext) 當請求經過Channel將入隊數據沖刷到遠程節點時被調用
write(ChannelHandlerContext, Object, ChannelPromise) 當請求經過Channel將數據寫到遠程節點時被調用

ChannelOutboundHandler中的大部分方法都須要一個ChannelPromise參數,方便在操做完成時獲取通知。ChannelPromise時ChannelFuture的一個子類,定義了一些可寫方法,如setSuccess()和setFailure()方法測試

ChannelHandler適配器

ChannelInboundHandlerAdapter和ChannelOutboundHandlerAdapter兩個適配器分別提供了ChannelInboundHandler和ChannelOutboundHandler的基本實現。經過擴展抽象類ChannelHandlerAdapter,它們得到了它們共同的超接口ChannelHandler的方法。spa

ChannelHandlerAdapter提供了isSharable(),若是其對應的實現被註解標註爲Sharable,這方法將返回true,表示它能夠被添加到多個ChannelPipeline。

ChannelInboundHandlerAdapter和ChannelOutboundHandlerAdapter中的方法體調用了其相關聯的ChannelHandlerContext上的等效方法,從而將事件轉發到了ChannelPipeline中的下一個ChannelHandler中。

資源管理

Netty目前定義了4種泄露檢測級別:

級別 描述
DISABLED 禁用泄漏檢測。只有在詳盡的測試以後才應設置爲這值
SIMPLE 使用1%的默認採樣率檢測並報告任何發現的泄漏。這是默認級別,適合絕大部分狀況
ADVANCED 使用默認的採樣率,報告所發現的任何的泄漏以及對應的消息被訪問的位置
PARANOID 相似於ADVANCED,可是其將會對每次(對消息的)訪問都進行採樣。會對性能有很大影響,只能在調試階段使用

java -Dio.netty.leakDetectionLevel=ADVANCED

若是一個消息被消費或者丟棄了,而且沒有傳遞給ChannelPipeline中的下一個ChannelOutboundHandler,那麼用戶就有責任調用ReferenceCountUtil.release()。若是消息到達了實際的傳輸層,那麼當它被寫入時或者Channel關閉時,都將被自動釋放。

ChannelPipeline接口

每個新建立的Channel都將會被分配一個新的ChannelPipeline。這項關聯是永久性的;Channel既不能附加另一個ChannelPipeline,也不能分離當前的。

根據事件的起源,事件將會被ChannelInboundHandler或者ChannelOutboundHandler處理。隨後,會調用ChannelHandlerContext實現,它將被轉發給同一超類型的下一個ChannelHandler。

ChannelHandlerContext使ChannelHandler可以和它的ChannelPipeline以及其餘的ChannelHandler交互。ChannelHandler能夠通知其所屬的ChannelPipeline中的下一個ChannelHandler,甚至能夠動態修改它所屬的ChannelPipeline。

在ChannelPipeline傳播事件時,它會測試ChannelPipeline中的下一個ChannelHandler的類型是否和事件的運動方向相匹配。若是不匹配,ChannelPipeline將跳過該ChannelHandler並前進到下一個,直到它找到和該事件所指望的方向相匹配的爲止。(ChannelHandler能夠同時實現ChannelInboundHandler和ChannelOutboundHandler接口

修改ChannelPipeline

ChannelHandler能夠經過添加、刪除或者替換其餘的ChannelHandler來實時地修改ChannelPipeline的佈局。

名稱 描述
addFirst(),addBefore(),addAfter(),addLast() 將一個ChannelHandler添加到ChannelPipeline
remove() 將一個ChannelHandler從ChannelPipeline中移除
replace() 將ChannelPipeline中的一個ChannelHandler替換爲另外一個ChannelHandler
get() 經過類型或者名稱返回ChannelHandler
context() 返回和ChannelHandler綁定的ChannelHandlerContext
names() 返回ChannelPipeline中全部的ChannelHandle的名稱

觸發事件

ChannelPipeline的API公開了用於調用入站和出站操做的附加方法,用於通知ChannelInboundHandler在ChannelPipeline中所發生的事件。

名稱 描述
fireChannelRegistered 調用ChannelPipeline中下一個ChannelInboundHandler的channelRegistered(ChannelHandlerContext)方法
fireChannelUnregistered 調用ChannelPipeline中下一個ChannelInboundHandler的channelUnregistered(ChannelHandlerContext)方法
fireChannelActive 調用ChannelPipeline中下一個ChannelInboundHandler的channelActive(ChannelHandlerContext)
fireChannelInActive 調用ChannelPipeline中下一個ChannelInboundHandler的channelInactive(ChannelHandlerContext)方法
fireExceptionCaught 調用ChannelPipeline中下一個ChannelInboundHandler的exceptionCaught(ChannelHandlertext, Throwable)方法
fireUserEventTriggerd 調用ChannelPipeline中下一個ChannelInboundHandler的userEventTriggered(ChannelHandlertext, Object)方法
fireChannelRead 調用ChannelPipeline中下一個ChannelInboundHandler的channelRead(ChannelHandlertext, Object msg)方法
fireChannelReadComplete 調用ChannelPipeline中下一個ChannelInboundHandler的channelReadComplete(ChannelHandlertext)方法
fireChannelWritabilityChanged 調用ChannelPipeline中下一個ChannelInboundHandler的channelWritabilityChanged(ChannelHandlertext)方法

ChannelPipelin的出站操做

名稱 描述
bind 將Channel綁定到一個本地地址,將調用ChannelPipeline中的下一個ChannelOutboundHandler的bind(ChannelHandlerContext,Socket,ChannelPromise)方法
connect 將Channel鏈接到一個遠程地址,這將調用ChannelPipeline中的下一個ChannelOutboundHandler的connect(ChannelHandlerContext,Socket,ChannelPromise)方法
disconnect 將Channel斷開鏈接。這將調用ChannelPipeline中的下一個ChannelOutboundHandler的disconnect(ChannelHandlerContext,Socket,ChannelPromise)方法
close 將Channel關閉。這將調用ChannelPipeline中的下一個ChannelOutboundHandler的close(ChannelHandlerContext,ChannelPromise)方法
deregister 將Channel從它先前分配的EventExecutor(即EventLoop)中註銷,這將調用ChannelPipeline中的下一個ChannelOutboundHandler的deregister(ChannelHandlerContext,ChannelPromise)方法
flush 沖刷Channel全部掛起的寫入。這將調用ChannelPipeline中的下一個ChannelOutboundHandler的flush(ChannelHandlerContext)方法
write 將消息寫入Channel。這將調用ChannelPipeline中的下一個ChannelOutboundHandler的write(ChannelContext,Object msg,ChannelPromise)方法。這並不會將消息寫入底層的Socket,而只會將它放入到隊列中。要將它寫入到Socket,須要調用flush或者writeAndFlush方法
writeAndFlush 先調用write再調用flush的便利方法
read 請求從Channel中讀取更多的數據。這將調用ChannelPipeline中的下一個ChannelOutboundHandler的read(ChannelHandlerContext)方法
  1. ChannelPipeline保存了與Channel相關聯的ChannelHandler
  2. ChannelPipeline能夠根據須要,經過添加或者刪除ChannelHandler來動態修改
  3. ChannelPipeline有着豐富的API調用,以響應入站和出站事件

ChanneHandlerContext接口

ChannelHandlerContext表明了ChannelHandler和ChannelPipeline之間的關聯。每當有ChannelHandler添加到ChannelPipeline中時,都會建立ChannelHandlerContext。ChannelHandlerContext的主要功能是管理它所關聯的ChannelHandler和在同一個ChannelPipeline中的其餘ChannelHandler之間的交互。

ChannelHandlerContext有不少方法,其中一些方法也存在於Channel和ChannelPipeline自己上。若是調用Channel或者ChannelPipeline上的方法,它們將沿着整個ChannelPipeline進行傳播,而調用ChannelHandlerContext上的相同方法,則從當前所關聯的ChannelHandler開始,而且只會傳播給位於該ChannelHandler的下一個可以處理該事件的ChannelHandler。

方法名稱 描述
alloc 返回和這個實例相關的Channel所配置的ByteBufAllocator
bind 綁定到給定的SocketAddress,並返回ChannelFuture
channel 返回綁定到這個實例的Channel
close 關閉Channel,並返回ChannelFuture
connect 鏈接給定的SocketAddress,並返回ChannelFuture
deregister 從以前分配的EventExecutor註銷,並返回ChannelFuture
disconnect 從遠程節點斷開,並返回ChannelFuture
executor 返回調度事件的EventExecutor
fireChannelActive 觸發對下一個ChannelInboundHandler上的channelActive()方法的調用
fireChannelInActive 觸發下一個ChannelInboundHandler上的channelInActive()方法
fireChannelRead 觸發對下一個ChannelInboundHandler上的channelRead()方法
fireChannelReadComplete 觸發對下一個ChannelInboundHandler上的channelReadComplete()方法的調用
fireChannelRegistered 觸發對下一個ChanneInboundHandler上的fireChannelRegistered方法的調用
fireChannelUnregistered 觸發對下一個ChannelInboundHandler上的fireChannelUnregistered方法的調用
fireChannelWritabilityChanged 觸發對下一個ChannelInboundHandler上的fireChannelWritabilityChanged方法的調用
fireExceptionCaught 觸發對下一個ChannelInboundHandler上的fireExceptionCaught方法的調用
fireUserEventTriggered 觸發對下一個ChannelInboundHandler上的fireUserEventTriggered(Object evt)方法的調用
handler 返回綁定到這個實例的ChannelHandler
isRemoved 若是從關聯的ChannelHandler已經被從ChannelPipeline中移除則返回true
name 返回這個實例的惟一名稱
pipeline 返回這個實例相關聯的ChannelPipeline
read 將數據從Channel讀取到第一個入站緩衝區;若是讀取成功則觸發一個channelRead事件,並(在最後一個消息被被讀取完成後)通知ChannelInboundHandler的channelReadComplete(ChannelHandlerContext)方法
write 經過這個實例寫入消息並通過ChannelPipeline
writeAndFlush 經過這個實例寫入並沖刷消息並通過ChannelPipeline
  1. ChannelHanlderContext和ChannelHandler之間的關聯是永遠不會改變的,因此緩存對它的引用是安全的
  2. ChannelHandlerContext上的方法產生的事件流更短,應該利用這個特性儘量的得到最大的性能

使用ChannelHandlerContext

img

讓ChannePipeline從某個特定的點開始傳播的緣由:

  1. 爲了減小將事件傳經對它不感興趣的ChannelHandler所帶來的開銷
  2. 爲了減小將事件傳經那些可能會對它感興趣的ChannelHandler

要想調用從某個特定的ChannelHandler開始處理的過程,必須獲取到在(ChannelPipeline)該ChannelHandler以前的hannelHandler所關聯的ChannelHandlerContext。這個ChannelHandlerContext將調用和它所關聯的ChannelHandler以後的ChannelHandler。

img

一個ChannelHandler能夠從屬於多個ChannelPipeline,因此它(同一個ChannelHandler)也能夠綁定到多個ChannelHandlerContext實例。用於這種用法的ChannelHandler必須使用@Shareable註解標註;不然將它添加到多個ChannelPipeline時將會觸發異常。顯而易見,爲了安全地被用於多個併發的Channel(即鏈接),這樣的ChannelHandler必須是線程安全的。

爲什麼要共享一個ChannelHandler?
在多個ChannelPipeline中安裝同一個ChannelHandler的一個常見緣由是用於收集跨越多個Channel的統計信息。

異常處理

處理入站異常

若是在處理入站事件的過程當中有異常被拋出,那麼它將從它在ChannelInboundHandler裏被觸發的那一點開始流經ChannelPipeline。若是想要處理這種類型的入站異常,必須在你的ChannelInboundHandler中重寫exceptionCaught(ChannelHandlerContext,Throwable)

異常會按照入站方向流動,因此通常處理異常的邏輯一般都在最後一個ChannelInboundHandler中實現

  1. ChannelHandler.exceptionCaught()的默認實現是簡單的將當前異常轉發給ChannelPipeline中的下一個ChannelHandler
  2. 若是異常到達了ChannelPipeline的尾端,它將會被記錄爲未被處理
  3. 要想定義自定義的處理邏輯,你須要重寫exceptionCaught()方法,而後決定是否將該異常傳播出去。

處理出站異常

用於處理出站操做中的正常完成以及異常的選項,都基於如下的通知機制:

  1. 每一個出站操做都返回一個ChannelFuture。註冊到ChannelFuture的ChannelFutureListener將在操做完成時被通知該操做是成功了仍是出錯了。
  2. 幾乎全部的ChannelOutboundHandler上的方法都會傳入一個ChannelPromise的實例。做爲ChannelFuture的子類,ChannelPromise也能夠被分配用於異步通知的監聽器。
  3. 添加ChannelFuturListener只須要調用ChannelFuture實例上的addListener(ChannelFutureListener)方法,而且有兩種不一樣的方式去實現。第一種是調用出站操做(如write方法)所返回的ChanneFuture上的addListener方法;第二種方式是將ChannelFutureListener添加到即將做爲參數傳遞給ChannelOutboundHandler的方法的ChannelPromise
  4. 經過調用ChannelPromise上的setSuccess和setFailure方法,可使一個操做的狀態在ChannelHandler的方法返回給其調用者時即刻被感知到。
相關文章
相關標籤/搜索
本站公眾號
   歡迎關注本站公眾號,獲取更多信息