Netty源碼解析5-ChannelHandler

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

更多文章關注:多線程/集合/分佈式/Netty/NIO/RPCgithub

ChannelHandler並不處理事件,而由其子類代爲處理:ChannelInboundHandler攔截和處理入站事件,ChannelOutboundHandler攔截和處理出站事件。ChannelHandler和ChannelHandlerContext經過組合或繼承的方式關聯到一塊兒成對使用。事件經過ChannelHandlerContext主動調用如fireXXX()和write(msg)等方法,將事件傳播到下一個處理器。注意:入站事件在ChannelPipeline雙向鏈表中由頭到尾正向傳播,出站事件則方向相反。
當客戶端鏈接到服務器時,Netty新建一個ChannelPipeline處理其中的事件,而一個ChannelPipeline中含有若干ChannelHandler。若是每一個客戶端鏈接都新建一個ChannelHandler實例,當有大量客戶端時,服務器將保存大量的ChannelHandler實例。爲此,Netty提供了Sharable註解,若是一個ChannelHandler狀態無關,那麼可將其標註爲Sharable,如此,服務器只需保存一個實例就能處理全部客戶端的事件。面試

核心類圖

上圖是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標記註解:markdown

@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。

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

                   關注公衆號,內推,面試,資源下載,關注更多大數據技術~
                   大數據成神之路~預計更新500+篇文章,已經更新60+篇~