动态

详情 返回 返回

Netty源碼-責任鏈模式運用 - 动态 详情

Netty基本介紹,參考juejin.cn/post/740884…

1、Netty的責任鏈模式

1.1 責任鏈模式實現樣例

基於上圖,寫一個責任鏈模式的案例如下:

從下面的例子我們可以知道,責任鏈模式包含下面幾個重要的部分:

  • HandlerChainContext:hander上下文,也就是責任鏈中的節點,持有一個handler,並有指向下一個節點的指針
  • Handler: 責任處理器
  • Pipeline:維護整個責任鏈,添加刪除責任處理器
public class Pipeline {
    //handler上下文,維護鏈表和負責鏈表的執行
    class HandlerChainContext {
        HandlerChainContext next;// 持有下一個節點:單鏈表
        AbstractHandler handler;
        public HandlerChainContext(AbstractHandler handler) {
            this.handler = handler;
        }
        // 將節點持有下去
        void handler(Object arg0) {
            this.handler.doHandler(this, arg0);
        }
        // 繼續執行下一個
        void runNext(Object arg0) {
            if (this.next != null) {
                this.next.handler(arg0);
            }
        }
    }
    // 持有上下文(可以獲得需要的數據,屬性)
    public HandlerChainContext context = new HandlerChainContext(new AbstractHandler() {
        @Override
        void doHandler(HandlerChainContext context, Object arg0) {
            System.out.println("折扣前"+arg0);
            context.runNext(arg0);
        }
    });

    // 添加責任鏈
    public void addLast(AbstractHandler handler) {
        HandlerChainContext next = context;
        while (next.next != null) {
            next = next.next;
        }
        next.next = new HandlerChainContext(handler);
    }

    // 開始調用
    public void requestProcess(Object arg0) {
        context.handler(arg0);
    }
    //處理器抽象類
    static abstract class AbstractHandler {
        abstract void doHandler(HandlerChainContext context, Object arg0);
    }
    //具體的處理器實現類(購物折扣1)
    static class Handler1 extends AbstractHandler {
        @Override
        void doHandler(HandlerChainContext handlerChainContext, Object arg0) {
            System.out.println("--首次購買打9折!");
            arg0 = new DecimalFormat("0.00").format(Double.valueOf(arg0.toString())*0.9);
            System.out.println("折扣後金額:"+arg0);
            // 繼續執行下一個
            handlerChainContext.runNext(arg0);
        }
    }
    //具體的處理器實現類(購物折扣2)
    static class Handler2 extends AbstractHandler {
        @Override
        void doHandler(HandlerChainContext handlerChainContext, Object arg0) {
            System.out.println("--滿200減20!");
            if(Double.valueOf(arg0.toString()) >= 200){
                arg0 = Double.valueOf(arg0.toString())-20;
                // 繼續執行下一個
                System.out.println("折扣後金額:"+arg0);
                handlerChainContext.runNext(arg0);
            }else{
                System.out.println("不滿足條件,折扣結束:"+arg0);
            }
        }
    }
    //具體的處理器實現類(購物折扣3)
    static class Handler3 extends AbstractHandler {
        @Override
        void doHandler(HandlerChainContext handlerChainContext, Object arg0) {
            System.out.println("--第二件減10元!");
            arg0 = Double.valueOf(arg0.toString())-10;
            System.out.println("折扣後金額:"+arg0);
            // 繼續執行下一個
            handlerChainContext.runNext(arg0);
        }
    }

    public static void main(String[] args) {
        Pipeline p = new Pipeline();
        p.addLast(new Handler1());
        p.addLast(new Handler2());
        p.addLast(new Handler3());
        p.requestProcess("150");
    }
}

1.2 Netty的責任處理接口

如下圖:責任處理接口分為

1.2.1 ChannelInboundHandler

入站處理器方法(註冊、渠道讀、渠道激活等等)

1.2.2 ChannelOutboundHandler

出站處理器方法(綁定、連接、讀、寫、刷新等等)

1.3 責任添加刪除接口ChannelPipeline

該接口的唯一實現類DefaultChannelPipeline,該類有HeadContext 和TailContext屬性,分別執行責任鏈的頭和尾節點。

1.4 上下文ChannelHandlerContext

該接口有一個抽象類實現AbstractChannelHandlerContext,這個類有prev和next屬性,指向責任鏈的前一個節點和後一個節點。整個責任鏈是雙鏈表結構。

該接口的handler() 方法返回Context持有的責任處理器handler。

1.5 責任終止機制

  • 在pipeline中任意一個節點,只要我們不手動的往下傳播,這個事件就會終止傳播在當前節點
  • 對於入站數據,默認會傳遞到尾結點,進行回收,如果我們不進行下一步傳播,事件就會終止在當前節點,別忘記收回msg
  • 對於出站數據,用Header節點的使用Unsafe對象,把數據寫回客户端也意味着終止

2、Netty中責任鏈如何運作?

2.1 我們看一個Netty源碼的bind方法樣例

我們看一下ServerBootstrap.doBind()方法,所有的bind最終轉到這裏。繼續進入doBind0,如圖:

進入下圖的channel.bind

2.2 AbstractChannel#bind,調用pipeline,這是責任鏈模式

2.3 繼續進入,我們來到AbstractChannelHandlerContext#bind

看下面紅圈的代碼,findContextOutbound這行代碼用來計算下一步執行責任鏈上哪一個節點,通過這種方式把責任鏈執行連起來,後續繼續執行就可以,handler的方法都是這樣結構的

2.4 掩碼MASK_BIND(責任鏈節點集合,不同責任鏈執行不同節點)

這兩個常量來自下面這個類,我們看到還有很多其他常量,這裏表示不同的事件,每個事件佔一位,一共16位。

MASK_ONLY_OUTBOUND是出站事件組合出來的,表示出站需要執行的責任節點。不同的掩碼錶示不同業務的責任鏈節點集合。

2.5 尋找責任鏈下一個節點AbstractChannelHandlerContext#findContextInbound

下圖是尋找責任鏈節點的方法,先獲取當前節點的下一個節點,然後判斷下一個節點是否要執行,不需要的話繼續下一個,直到找到需要執行的節點。

這裏從一個節點走向下一個節點,直到走完真個責任鏈。

我們繼續看while循環的判斷條款skipContext,Context使用傳入的mask,也就是MASK_BIND和MASK_ONLY_OUTBOUND做與運算,如果是0的話,説明MASK_ONLY_OUTBOUND不包含綁定操作,該節點不執行,跳過

2.6 執行handler

找到要執行的責任節點後,根據handler類型執行不同的handler的bind方法完成綁定

3、總結:

Netty通過責任鏈的設計模式是的對事件的處理,符合開閉原則,對新增處理事件開放,對修改責任鏈邏輯封閉。

將責任處理器分為入站出站的結構很清晰,使用MASK對Handler分類處理,實現了一個輕量級的分類管理,也加快了處理速度。

Add a new 评论

Some HTML is okay.