狀態 | 描述 |
---|---|
ChannelUnregistered | Channel已經被建立,但未註冊到EventLoop |
ChannelRegistered | Channel已經被註冊到了EventLoop |
ChannelActive | Channel處於活動狀態(已經鏈接到它的遠程節點)。如今Channel能夠接受和發送數據 |
ChannelInActive | Channel沒有鏈接到遠程節點 |
通常Channel的生命週期順序ChannelRegistered -> ChannelActive -> ChannelInactive -> ChannelUnregistered。java
當Channel的狀態發生變化時,將會生成對應的事件。與此同時,這些事件會被轉發給ChannelPipeline中的ChannelHandler。緩存
ChannelHandler定義的生命週期操做,在ChannelHandler被添加到ChannelPipeline中或者被從ChannelPipeline中移除時會調用這些方法。這些方法中均可以接受一個ChannelHandlerContext參數。安全
類型 | 描述 |
---|---|
handlerAdded | 當把ChannelHandler添加到ChannelPipeline中時被調用 |
handlerRemoved | 當從ChannelHandler在ChannelPipeline移除時調用 |
exceptionCaught | 當處理過程當中在ChannelPipeline中有錯誤產生時被調用 |
Netty中定義了下面兩個重要的ChannelHandler接口:併發
類型 | 描述 |
---|---|
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處理。它的方法將被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()方法測試
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關閉時,都將被自動釋放。
每個新建立的Channel都將會被分配一個新的ChannelPipeline。這項關聯是永久性的;Channel既不能附加另一個ChannelPipeline,也不能分離當前的。
根據事件的起源,事件將會被ChannelInboundHandler或者ChannelOutboundHandler處理。隨後,會調用ChannelHandlerContext實現,它將被轉發給同一超類型的下一個ChannelHandler。
ChannelHandlerContext使ChannelHandler可以和它的ChannelPipeline以及其餘的ChannelHandler交互。ChannelHandler能夠通知其所屬的ChannelPipeline中的下一個ChannelHandler,甚至能夠動態修改它所屬的ChannelPipeline。
在ChannelPipeline傳播事件時,它會測試ChannelPipeline中的下一個ChannelHandler的類型是否和事件的運動方向相匹配。若是不匹配,ChannelPipeline將跳過該ChannelHandler並前進到下一個,直到它找到和該事件所指望的方向相匹配的爲止。(ChannelHandler能夠同時實現ChannelInboundHandler和ChannelOutboundHandler接口)
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)方法 |
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 |
讓ChannePipeline從某個特定的點開始傳播的緣由:
要想調用從某個特定的ChannelHandler開始處理的過程,必須獲取到在(ChannelPipeline)該ChannelHandler以前的hannelHandler所關聯的ChannelHandlerContext。這個ChannelHandlerContext將調用和它所關聯的ChannelHandler以後的ChannelHandler。
一個ChannelHandler能夠從屬於多個ChannelPipeline,因此它(同一個ChannelHandler)也能夠綁定到多個ChannelHandlerContext實例。用於這種用法的ChannelHandler必須使用@Shareable註解標註;不然將它添加到多個ChannelPipeline時將會觸發異常。顯而易見,爲了安全地被用於多個併發的Channel(即鏈接),這樣的ChannelHandler必須是線程安全的。
爲什麼要共享一個ChannelHandler?
在多個ChannelPipeline中安裝同一個ChannelHandler的一個常見緣由是用於收集跨越多個Channel的統計信息。
若是在處理入站事件的過程當中有異常被拋出,那麼它將從它在ChannelInboundHandler裏被觸發的那一點開始流經ChannelPipeline。若是想要處理這種類型的入站異常,必須在你的ChannelInboundHandler中重寫exceptionCaught(ChannelHandlerContext,Throwable)
異常會按照入站方向流動,因此通常處理異常的邏輯一般都在最後一個ChannelInboundHandler中實現
用於處理出站操做中的正常完成以及異常的選項,都基於如下的通知機制: