Netty原始碼解析5-ChannelHandler

王知無發表於2019-02-22

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

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

ChannelHandler並不處理事件,而由其子類代為處理:ChannelInboundHandler攔截和處理入站事件,ChannelOutboundHandler攔截和處理出站事件。ChannelHandler和ChannelHandlerContext通過組合或繼承的方式關聯到一起成對使用。事件通過ChannelHandlerContext主動呼叫如fireXXX()和write(msg)等方法,將事件傳播到下一個處理器。注意:入站事件在ChannelPipeline雙向連結串列中由頭到尾正向傳播,出站事件則方向相反。 當客戶端連線到伺服器時,Netty新建一個ChannelPipeline處理其中的事件,而一個ChannelPipeline中含有若干ChannelHandler。如果每個客戶端連線都新建一個ChannelHandler例項,當有大量客戶端時,伺服器將儲存大量的ChannelHandler例項。為此,Netty提供了Sharable註解,如果一個ChannelHandler狀態無關,那麼可將其標註為Sharable,如此,伺服器只需儲存一個例項就能處理所有客戶端的事件。

核心類圖

Netty原始碼解析5-ChannelHandler

上圖是ChannelHandler的核心類類圖,其繼承層次清晰,我們逐一分析。

1.ChannelHandler

ChannaleHandler 作為最頂層的介面,並不處理入站和出站事件,所以介面中只包含最基本的方法:

// Handler本身被新增到ChannelPipeline時呼叫
    void handlerAdded(ChannelHandlerContext ctx) throws Exception;
    // Handler本身被從ChannelPipeline中刪除時呼叫
    void handlerRemoved(ChannelHandlerContext ctx) throws Exception;
    // 發生異常時呼叫
    void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception;
複製程式碼

其中也定義了Sharable標記註解:

 @Inherited
    @Documented
    @Target(ElementType.TYPE)
    @Retention(RetentionPolicy.RUNTIME)
    @interface Sharable {
        // no value
    }
複製程式碼

作為ChannelHandler的預設實現,ChannelHandlerAdapter有個重要的方法isSharable(),程式碼如下:

 public boolean isSharable() {
        Class<?> clazz = getClass();
        // 每個執行緒一個快取
        Map<Class<?>, Boolean> cache = 
                InternalThreadLocalMap.get().handlerSharableCache();
        Boolean sharable = cache.get(clazz);
        if (sharable == null) {
            // Handler是否存在Sharable註解
            sharable = clazz.isAnnotationPresent(Sharable.class);
            cache.put(clazz, sharable);
        }
        return sharable;
    }
複製程式碼

這裡引入了優化的執行緒區域性變數InternalThreadLocalMap,將在以後分析,此處可簡單理解為執行緒變數ThreadLocal,即每個執行緒都有一份ChannelHandler是否Sharable的快取。這樣可以減少執行緒間的競爭,提升效能。

2.ChannelInboundHandler

ChannelInboundHandler處理入站事件,以及使用者自定義事件:

    // 類似的入站事件
    void channeXXX(ChannelHandlerContext ctx) throws Exception;
    // 使用者自定義事件
    void userEventTriggered(ChannelHandlerContext ctx, Object evt) throws Exception;
複製程式碼

ChannelInboundHandlerAdapter作為ChannelInboundHandler的實現,預設將入站事件自動傳播到下一個入站處理器。其中的程式碼高度一致,如下:

    public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {
        ctx.fireChannelRead(msg);
    }
複製程式碼

3.ChannelOutboundHandler

ChannelOutboundHandler處理出站事件:

// 類似的出站事件
    void read(ChannelHandlerContext ctx) throws Exception;
複製程式碼

同理,ChannelOutboundHandlerAdapter作為ChannelOutboundHandler的事件,預設將出站事件傳播到下一個出站處理器:

    @Override
    public void read(ChannelHandlerContext ctx) throws Exception {
        ctx.read();
    }
複製程式碼

4.ChannelDuplexHandler

ChannelDuplexHandler則同時實現了ChannelInboundHandler和ChannelOutboundHandler介面。如果一個所需的ChannelHandler既要處理入站事件又要處理出站事件,推薦繼承此類。 至此,ChannelHandler的核心類已分析完畢,接下來將分析一些Netty自帶的Handler。

Netty原始碼解析5-ChannelHandler

	請戳GitHub原文: https://github.com/wangzhiwubigdata/God-Of-BigData

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

相關文章