Netty原始碼解析4-Handler綜述

王知無發表於2019-03-02

請戳GitHub原文: github.com/wangzhiwubi…

更多文章關注:多執行緒/集合/分散式/Netty/NIO/RPC

Netty中的Handler簡介

Handler在Netty中,佔據著非常重要的地位。Handler與Servlet中的filter很像,通過Handler可以完成通訊報文的解碼編碼、攔截指定的報文、

統一對日誌錯誤進行處理、統一對請求進行計數、控制Handler執行與否。一句話,沒有它做不到的只有你想不到的

  Netty中的所有handler都實現自ChannelHandler介面。按照輸入輸出來分,分為ChannelInboundHandlerChannelOutboundHandler兩大類

ChannelInboundHandler對從客戶端發往伺服器的報文進行處理,一般用來執行解碼、讀取客戶端資料、進行業務處理等;ChannelOutboundHandler

對從伺服器發往客戶端的報文進行處理,一般用來進行編碼、傳送報文到客戶端

  Netty中可以註冊多個handler。ChannelInboundHandler按照註冊的先後順序執行;ChannelOutboundHandler按照註冊的先後順序逆序執行。

  • 全網唯一一個從0開始幫助Java開發者轉做大資料領域的公眾號~
  • 公眾號大資料技術與架構或者搜尋import_bigdata關注,大資料學習路線最新更新,已經有很多小夥伴加入了~
Netty原始碼解析4-Handler綜述

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物件例項與之對應。

Netty原始碼解析4-Handler綜述
	請戳GitHub原文: https://github.com/wangzhiwubigdata/God-Of-BigData

                   關注公眾號,內推,面試,資源下載,關注更多大資料技術~
                   大資料成神之路~預計更新500+篇文章,已經更新60+篇~ 
複製程式碼

相關文章