請戳GitHub原文: github.com/wangzhiwubi…
更多文章關注:多執行緒/集合/分散式/Netty/NIO/RPC
- Java高階特性增強-集合
- Java高階特性增強-多執行緒
- Java高階特性增強-Synchronized
- Java高階特性增強-volatile
- Java高階特性增強-併發集合框架
- Java高階特性增強-分散式
- Java高階特性增強-Zookeeper
- Java高階特性增強-JVM
- Java高階特性增強-NIO
- RPC
- zookeeper
- JVM
- NIO
- 其他更多
Netty中的Handler簡介
Handler
在Netty中,佔據著非常重要的地位。Handler
與Servlet中的filter很像,通過Handler可以完成通訊報文的解碼編碼、攔截指定的報文、
統一對日誌錯誤進行處理、統一對請求進行計數、控制Handler執行與否。一句話,沒有它做不到的只有你想不到的
Netty中的所有handler都實現自ChannelHandler介面。按照輸入輸出來分,分為ChannelInboundHandler
、ChannelOutboundHandler
兩大類
ChannelInboundHandler
對從客戶端發往伺服器的報文進行處理,一般用來執行解碼、讀取客戶端資料、進行業務處理等;ChannelOutboundHandler
對從伺服器發往客戶端的報文進行處理,一般用來進行編碼、傳送報文到客戶端
Netty中可以註冊多個handler。ChannelInboundHandler
按照註冊的先後順序執行;ChannelOutboundHandler
按照註冊的先後順序逆序執行。
- 全網唯一一個從0開始幫助Java開發者轉做大資料領域的公眾號~
- 公眾號大資料技術與架構或者搜尋import_bigdata關注,大資料學習路線最新更新,已經有很多小夥伴加入了~
ChannelPipeline中的事件不會自動流動,而我們一般需求事件自動流動,Netty提供了兩個Adapter:ChannelInboundHandlerAdapter和ChannelOutboundHandlerAdapter來滿足這種需求。其中的實現類似如下:
// inboud事件預設處理過程
public void channelRegistered(ChannelHandlerContext ctx) throws Exception {
ctx.fireChannelRegistered(); // 事件傳播到下一個Handler
}
// outboud事件預設處理過程
public void bind(ChannelHandlerContext ctx, SocketAddress localAddress,
ChannelPromise promise) throws Exception {
ctx.bind(localAddress, promise); // 事件傳播到下一個Handler
}
複製程式碼
在Adapter中,事件預設自動傳播到下一個Handler,這樣帶來的另一個好處是:使用者的Handler類可以繼承Adapter且覆蓋自己感興趣的事件實現,其他事件使用預設實現,不用再實現ChannelIn/outboudHandler介面中所有方法,提高效率。
我們常常遇到這樣的需求:在一個業務邏輯處理器中,需要寫資料庫、進行網路連線等耗時業務。Netty的原則是不阻塞I/O執行緒,所以需指定Handler執行的執行緒池,可使用如下程式碼:
static final EventExecutorGroup group = new DefaultEventExecutorGroup(16);
...
ChannelPipeline pipeline = ch.pipeline();
// 簡單非阻塞業務,可以使用I/O執行緒執行
pipeline.addLast("decoder", new MyProtocolDecoder());
pipeline.addLast("encoder", new MyProtocolEncoder());
// 複雜耗時業務,使用新的執行緒池
pipeline.addLast(group, "handler", new MyBusinessLogicHandler());
複製程式碼
ChannelHandler中有一個Sharable註解,使用該註解後多個ChannelPipeline中的Handler物件例項只有一個,從而減少Handler物件例項的建立。程式碼示例如下:
public class DataServerInitializer extends ChannelInitializer<Channel> {
private static final DataServerHandler SHARED = new DataServerHandler();
@Override
public void initChannel(Channel channel) {
channel.pipeline().addLast("handler", SHARED);
}
}
複製程式碼
Sharable註解的使用是有限制的,多個ChannelPipeline只有一個例項,所以該Handler要求無狀態。上述示例中,DataServerHandler的事件處理方法中,不能使用或改變本身的私有變數,因為ChannelHandler是非執行緒安全的,使用私有變數會造成執行緒競爭而產生錯誤結果。
ChannelHandlerContext
Context指上下文關係,ChannelHandler的Context指的是ChannleHandler之間的關係以及ChannelHandler與ChannelPipeline之間的關係。ChannelPipeline中的事件傳播主要依賴於ChannelHandlerContext實現,由於ChannelHandlerContext中有ChannelHandler之間的關係,所以能得到ChannelHandler的後繼節點,從而將事件傳播到下一個ChannelHandler。
ChannelHandlerContext繼承自AttributeMap,所以提供了attr()方法設定和刪除一些狀態屬性值,使用者可將業務邏輯中所需使用的狀態屬性值存入到Context中。此外,Channel也繼承自AttributeMap,也有attr()方法,在Netty4.0中,這兩個attr()方法並不等效,這會給使用者程式設計師帶來困惑並且增加記憶體開銷,所以Netty4.1中將channel.attr()==ctx.attr()。在使用Netty4.0時,建議只使用channel.attr()防止引起不必要的困惑。
一個Channel對應一個ChannelPipeline,一個ChannelHandlerContext對應一個ChannelHandler,但一個ChannelHandler可以對應多個ChannelHandlerContext。當一個ChannelHandler使用Sharable註解修飾且新增同一個例項物件到不用的Channel時,只有一個ChannelHandler例項物件,但每個Channel中都有一個ChannelHandlerContext物件例項與之對應。
請戳GitHub原文: https://github.com/wangzhiwubigdata/God-Of-BigData
關注公眾號,內推,面試,資源下載,關注更多大資料技術~
大資料成神之路~預計更新500+篇文章,已經更新60+篇~
複製程式碼