業務邏輯的鏈式處理怎麼設計?Tomcat和Netty是如何實現過濾器鏈和事件鏈?

在業務開發中,對於同一類邏輯處理,一般都是都是歸類爲一組集合,類似於一條業務鏈來處理,流程如下:

舉個例子,在下單過程中,會對生成訂單進行很多條件判斷。通常,會定義一個校驗接口Validator定義生成訂單的條件判斷業務的抽象;在下單過程因爲有很多不同的條件判斷,所以,就會在下單的服務中,定義類似於一組的校驗業務,例如,定義集合List<Validator> validators。

如果,在鏈式業務處理過程中,對於不同的訂單會有校驗邏輯需要滿足條件才能判斷,這時如何進行過濾呢?如果在不同的下單場景需要動態的添加或者修改一些校驗條件,應該如何操作呢?有的業務有不同的優先級和先後順序,應該如何設計?下面以tomcat中Filter過濾器鏈,Netty的事件鏈式處理爲例,來介紹常見的鏈式設計。tomcat的filter過濾器鏈基於List集合,Netty的事件鏈表基於鏈表實現。

tomcat中Filter過濾器的實現

在tomcat中,對於Http請求通常會進行用戶身份的校驗,權限的驗證,日誌處理等等業務的攔截和過濾。模仿tomcat實現的僞代碼,定義Filter接口實現過濾業務的封裝,CompositeFilter定義一組的過濾邏輯處理集合,FilterChain決定每次執行的FIlter的選擇邏輯,每次執行時,從條件過濾組中篩選滿足條件的過濾器Filter進行。過濾器鏈執行流程的大致如下:

 

Filter,CompositeFilter,FilterChain僞代碼定義如下:

/**
 * 過濾器
 **/
public interface Filter {
    /**
     * 過濾
     */
    void doFilter(ServletRequest var1, ServletResponse var2, FilterChain var3) throws IOException, ServletException;
}

/**
 * 過濾器每次執行的篩選定義
 **/
public interface FilterChain {
    void doFilter(ServletRequest var1, ServletResponse var2) throws IOException, ServletException;
}


/**
 * 過濾器集合定義,使用List集合進行封裝
 **/
public class CompositeFilter implements Filter {

    // 過濾器列表
    private List<? extends Filter> filters = new ArrayList();

    public CompositeFilter() {
    }

    public void setFilters(List<? extends Filter> filters) {
        this.filters = new ArrayList(filters);
    }

    /**
     * 過濾邏輯
     **/
    public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {
        (new CompositeFilter.VirtualFilterChain(chain, this.filters)).doFilter(request, response);
    }

  
    /**
	 * 內部類實現自定義的FilterChain,根據list集合的index,從0開始順序的選擇Filter進行處理
	 */
    private static class VirtualFilterChain implements FilterChain {
        private final FilterChain originalChain;
        private final List<? extends Filter> additionalFilters;
        // 起始篩選的index
        private int currentPosition = 0;

        public VirtualFilterChain(FilterChain chain, List<? extends Filter> additionalFilters) {
            this.originalChain = chain;
            this.additionalFilters = additionalFilters;
        }

        public void doFilter(ServletRequest request, ServletResponse response) throws IOException, ServletException {
            if (this.currentPosition == this.additionalFilters.size()) {
                this.originalChain.doFilter(request, response);
            } else {
                ++this.currentPosition;
                // 根據index從過濾器集合中選擇Filter
                Filter nextFilter = (Filter)this.additionalFilters.get(this.currentPosition - 1);
                nextFilter.doFilter(request, response, this);
            }
        }
    }
}

Netty中事件過濾器鏈的處理

在基於Socket的網絡編程中,當Socket綁定接口時,連接或斷開,讀取或者寫入數據時,都會有不同的業務需要處理。例如,從Socket讀取數據,或者寫人數據時,通常會進行數據的編碼節碼,數據的業務處理。Netty是對網絡編程的封裝,定義了ChannelHandler實現對各種事件的觸發的時的業務邏輯,定義ChannelPipeline基於鏈表集合實現對所有的事件處理邏輯封裝,ChannelHandlerContext基於前後事件的綁定,實現了對於每次執行事件觸發時,事件的選擇邏輯。其執行流程如下:

 

ChannelPipeline的默認實現類是DefaultChannelPipeline 在添加具體的ChannelHandler實現類時,會把ChannelHandler封裝爲AbstractChannelHandlerContext ,通過AbstractChannelHandlerContext 指定ChannelHandler前後的關聯節點。ChannelHandler,ChannelPipeline,ChannelHandlerContext接口源碼如下:

/**
 * 各類Socket事件的具體業務定義的父接口
 */
public interface ChannelHandler {

    /**
     * 新增handler
     */
    void handlerAdded(ChannelHandlerContext ctx) throws Exception;

    /**
     *刪除handler
     */
    void handlerRemoved(ChannelHandlerContext ctx) throws Exception;

    /**
     * 異常的處理
     */
    @Deprecated
    void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception;
}


/**
 * 基於鏈表實現AbstractChannelHandlerContext的封裝,基於頭部和尾部的數據數據添加,各類事件監聽的統一調用
 */
public class DefaultChannelPipeline implements ChannelPipeline {

    /**
     * 鏈表head與tail節點定義
     */
    final AbstractChannelHandlerContext head;
    final AbstractChannelHandlerContext tail;

    /**
     * 鏈表首部插入邏輯
     */
    private void addFirst0(AbstractChannelHandlerContext newCtx) {
        AbstractChannelHandlerContext nextCtx = head.next;
        newCtx.prev = head;
        newCtx.next = nextCtx;
        head.next = newCtx;
        nextCtx.prev = newCtx;
    }

   /**
     * 鏈表首部插尾部入邏輯
     */
    private void addLast0(AbstractChannelHandlerContext newCtx) {
        AbstractChannelHandlerContext prev = tail.prev;
        newCtx.prev = prev;
        newCtx.next = tail;
        prev.next = newCtx;
        tail.prev = newCtx;
    }

    @Override
    public void handlerAdded(ChannelHandlerContext ctx) throws Exception { }

    @Override
    public void handlerRemoved(ChannelHandlerContext ctx) throws Exception { }

    @Override
    public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception {
            onUnhandledInboundException(cause);
        }

   /**
     * 讀取數據事件邏輯
     */
    @Override
    public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {
            onUnhandledInboundMessage(msg);
        }

   /**
     * 寫入數據事件邏輯
     */
    @Override
    public void write(ChannelHandlerContext ctx, Object msg, ChannelPromise promise) throws Exception {
            unsafe.write(msg, promise);
        }

}

/**
  * 鏈表事件篩選邏輯
  */
abstract class AbstractChannelHandlerContext extends DefaultAttributeMap
        implements ChannelHandlerContext, ResourceLeakHint {


  /**
    * 讀取數據邏輯
    */
    @Override
    public ChannelHandlerContext fireChannelRead(final Object msg) {
        invokeChannelRead(findContextInbound(), msg);
        return this;
    }

 /**
    * 選取next指定的AbstractChannelHandlerContext執行
    */
 private AbstractChannelHandlerContext findContextInbound() {
        AbstractChannelHandlerContext ctx = this;
        do {
            ctx = ctx.next;
        } while (!ctx.inbound);
        return ctx;
    }

}

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章