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學習之ChannelHandler&ChannelPipeline
Netty學習之ChannelHandler&ChannelPipeline 前言 ChannelHandler Channel生命週期 ChannelUnregistered,Channel被建立,但是還沒有註冊到EventLoop中 Channel
netty學習之Reactor線程模型以及在netty中的應用
rec 直接 滿足 red 轉載 chan tail io處理 理論 轉載:http://blog.csdn.net/u010853261/article/details/55805216 說道netty的線程模型,我們第一反應就是經典的Reactor線程模型,下面我們就
Netty學習之路(六)-分隔符和定長解碼器的應用
之前已經使用了LineBasedFrameDecoder解決TCP粘包問題,現在再學兩種解決TCP粘包的方法。 DelimiterBasedFrameDecoder:可以自動完成以分隔符做結束標誌的訊息的解碼,分隔符自定義。 FixedLengthFrameDecoder:
Netty學習之路(五)-TCP粘包/拆包問題
TCP是個“流協議”,所謂流,就是沒有界限的一串資料。TCP底層並不瞭解上層業務資料的具體含義,它會根據TCP緩衝區的實際情況進行包的劃分,所以一個完整的包可能會被TCP拆分成多個包進行傳送,也有可能吧多個小的包封裝成一個大的資料包傳送,這就是TCP粘包和拆包問題。 TCP粘包/拆包產生
Netty學習之路(四)-Netty入門實戰
前面學習了用Java原生NIO的程式設計實踐,過程還是挺複雜的,需要熟練掌握Selector,ServerSocketChannel握,SocketChannel,ByteBuffer等。所以在絕大多數業務場景中我們可以使用Netty來進行NIO程式設計。先總結一下Netty的優點:
Netty學習之路(三)-AIO程式設計
NIO 2.0引入了新的非同步通道的概念,與之前非阻塞IO(NIO)不同的是,NIO 2.0非同步套接字通道是真正的非同步非阻塞I/O,對應於UNIX網路程式設計中的事件驅動I/O(AIO)。它不需要通過多路複用器對註冊的通道進行輪詢操作即可實現非同步讀寫,從而簡化了NIO的程式設計模型。話
Netty學習之路(九)-JBoss Marshalling編解碼
JBoss Marshalling 是一個Java物件序列化包,對JDK預設的序列化框架進行了優化,但又保持跟java.io.Serializable介面的相容,同時增加了一些可調的引數和附加的特性。 Marshalling開發環境準備 下載相關的Marshalling類庫:地址,將
Netty學習之路(二)-非同步IO(NIO)程式設計
NIO到底是什麼簡稱?有人稱之為New I/O,原因為他相對於之前的I/O類庫來說是新增的。這是官方叫法。但是,由於之前老的I/O類庫是阻塞I/O,New I/O類庫的目標就是讓java支援非阻塞I/O,所以更多的人稱之為非阻塞I/O(Non-block I/O)。在開始進行NIO程式設計之
Netty學習之路(一)- 同步與非同步IO
本篇部落格主要是講一些基礎,記錄我的學習過程,同時嘗試養成寫部落格的習慣。內容基本來自Netty權威指南加上一丟丟的個人理解。。。。 I/O基礎入門 在jdk1.4以前,java對i/o的支援並不完善,開發人員在開發高效能i/o時會遇到巨大的挑戰與困難,主要問題如下: 沒
Netty學習之路(八)-Google Protobuf編碼
Protobuf是一個靈活,高效,結構化的資料序列化框架,相比於XML等傳統的序列化工具,它更小,更快,更簡單。Protobuf支援資料結構化一次可以到處使用,甚至可以跨語言使用,通過程式碼生成工具可以自動生成不同語言版本的原始碼,甚至可以在使用不同版本的資料結構程序間進行資料傳遞,實現資料
Netty學習之路(七)-編解碼技術
當進行遠端跨程序服務呼叫時,需要把被傳輸的Java物件編碼為位元組陣列或者ByteBuffer物件。而當遠端服務讀取到ByteBuffer物件或者位元組陣列時,需要將其解碼為傳送時的Java物件。這被稱為Java物件編解碼技術。而我們常見得Java序列化僅僅是Java編解碼技術的一種,由於j
Netty學習之旅----ThreadLocal原理分析與效能優化思考(思考篇)
/** * Returns the value in the current thread's copy of this * thread-local variable. If the variable has no value for the
Netty學習之路
同步與非同步IO (校招對我來說已經結束了,雖然找的並不是很滿意,但是已經不想再嘗試了,畢竟能力所限,不管去哪個公司還是先提高自己的水平吧,從今天開始先搞搞Netty)本篇部落格主要是講一些基礎,記錄我的學習過程,同時嘗試養成寫部落格的習慣。內容基本來自Net
Netty學習之路(五)
TCP粘包/拆包問題 TCP是個“流協議”,所謂流,就是沒有界限的一串資料。TCP底層並不瞭解上層業務資料的具體含義,它會根據TCP緩衝區的實際情況進行包的劃分,所以一個完整的包可能會被TCP拆分成多個包進行傳送,也有可能吧多個小的包封裝成一個大的資料包傳送,這
Netty學習之分隔符解決TCP粘包
package com.phei.netty.s20160424; import io.netty.bootstrap.ServerBootstrap; import io.netty.buffer.ByteBuf; import io.netty.buffer.Unpooled; import io.ne
Netty學習之旅----ByteBuf原始碼解讀之初探UnpooledHeapByteBuf、UnpooledDirectByteBuf
前沿: 在讀原始碼的過程中我發現有如下幾個點,感覺有問題: 1)UnpooledDirectByteBuf的 capacity(int newCapacity)方法,在readerIndex大於或等於newCapacity時,此時不需要將原ByteBuffer中的資
Netty學習之旅------原始碼分析Netty執行緒本地分配機制與PooledByteBuf執行緒級物件池原理分析
final PoolArena<byte[]> heapArena; //使用輪叫輪詢機制,每個執行緒從heapArena[]中獲取一個,用於記憶體分配。 final PoolArena<ByteBuffer> directArena;
Netty學習之旅----原始碼分析Netty記憶體洩漏檢測
1、圖說Netty直接記憶體管理 2、Netty 直接記憶體的使用示例 ByteBuf buf = Unpooled.directBuffer(512); System.out.println(buf); // Si
Netty學習之旅------原始碼分析Netty解碼編碼器實現原理
package io.netty.handler.codec; import io.netty.buffer.ByteBuf; import io.netty.buffer.Unpooled; import io.netty.channel.ChannelHandler; import io.netty.c
Netty學習之旅----原始碼分析記憶體分配與釋放原理
static PooledHeapByteBuf newInstance(int maxCapacity) { PooledHeapByteBuf buf = RECYCLER.get(); buf.setRefCnt(1); buf.maxCapacity(m