Netty的ChannelPipeline

1、如何理解Netty的ChannelPipeline
     在Netty中,每個Channel被創建的時候都需要被關聯一個對應的pipeline(通道),這種關聯關係是永久的(整個程序運行的生命週期中)。ChannelPipeline可以理解成一個消息( 或消息事件,ChanelEvent)流轉的通道,在這個通道中可以被附上許多用來處理消息的handler,當消息在這個通道中流轉的時候,如果有與這個消息類型相對應的handler,就會觸發這個handler去執行相應的動作。它實現了Intercepting Filter模式(個人理解與Filter Chain模式類似)。

可以通過下面這句話來理解一些相關的概念:ChannelEvents are processed by ChannelHandlers in a ChannelPipeline

2、ChannelPipeline的創建方式
     Netty的作者建議採用輔助類org.jboss.netty.channel.Channels的靜態無參函數pipeline來創建一個ChannelPipeline對象,從源代碼上看其實就是調用了DefaultChannelPipeline的無參構造函數,返回new出來的對象而已。

3、消息的流轉
     Netty中消息的流轉有兩個方向:上行、下行
     因此,處理消息事件(ChannelEvent)的處理器(ChannelHandler)大致可以分爲兩個方向:ChannelUpstreamHandler、ChannelDownstreamHandler
   
  *                                       I/O Request
 *                                     via {@link Channel} or
 *                                 {@link ChannelHandlerContext}
 *                                           |
 *  +----------------------------------------+--------------- +
 *  |                  ChannelPipeline       |               |
 *  |                                       \|/              |
 *  |  + ----------------------+  +----------- +------------+  |
 *  |  | Upstream Handler  N  |  | Downstream Handler  1  |  |
 *  |  + ----------+----------- +  +-----------+ ------------+  |
 *  |            /|\                         |               |
 *  |             |                         \|/              |
 *  |  + ----------+----------- +  +-----------+ ------------+  |
 *  |  | Upstream Handler N -1 |  | Downstream Handler  2  |  |
 *  |  + ----------+----------- +  +-----------+ ------------+  |
 *  |            /|\                         .               |
 *  |             .                          .               |
 *  |     [ sendUpstream() ]        [ sendDownstream() ]     |
 *  |     [ + INBOUND data ]        [ + OUTBOUND data  ]     |
 *  |             .                          .               |
 *  |             .                         \|/              |
 *  |  + ----------+----------- +  +-----------+ ------------+  |
 *  |  | Upstream Handler  2  |  | Downstream Handler M -1 |  |
 *  |  + ----------+----------- +  +-----------+ ------------+  |
 *  |            /|\                         |               |
 *  |             |                         \|/              |
 *  |  + ----------+----------- +  +-----------+ ------------+  |
 *  |  | Upstream Handler  1  |  | Downstream Handler  M  |  |
 *  |  + ----------+----------- +  +-----------+ ------------+  |
 *  |            /|\                         |               |
 *  +-------------+ --------------------------+--------------- +
 *                |                         \|/
 *  +-------------+ --------------------------+--------------- +
 *  |             |                          |               |
 *  |     [ Socket.read() ]          [ Socket.write() ]      |
 *  |                                                        |
 *  |  Netty Internal I/O Threads (Transport Implementation) |
 *  +--------------------------------------------------------+

以upstream爲例,從socket讀出來的數據,逐漸往上進行傳遞,即原始數據依次往上被進行解析,可能因爲所需要的業務數據外面包裹着許多其它附加的數據。如加密、驗證、簽名等。
downstream與此相反,即把業務數據進行一層層地加密、簽名等處理後,寫到socket中發出去。這個模型其實可以參考(TCP的七層模型)

4 、pipeline中的handlers順序如何理解
在添加handler的時候,如果理解順序。可以把pipeline理解成一個隊列,addLast是添加到隊列的尾部,addFirst是添加到隊列的頭部。
upstream的方向是從頭到尾,downstream的方向是從尾到頭。

5、 多線程環境
ChannelPipeline中的handler是可以在任何時候動態地增加或者刪除的,因爲ChannelPipeline本身是線程安全的(這點由Netty本身去保證)

6、 ChannelPipeline實例具體解析
在對ChannelPipeline進行解析時,以Netty提供的一個默認實現DefaultChannelPipeline來進行解析,先來看下這個類的一些成員變量(去除日誌相關成員logger)
     static final ChannelSink discardingSink = new DiscardingChannelSink();
    private volatile Channel channel;
    private volatile ChannelSink sink;
    private volatile DefaultChannelHandlerContext head;
    private volatile DefaultChannelHandlerContext tail;
    private final Map<String, DefaultChannelHandlerContext> name2ctx =
        new HashMap<String, DefaultChannelHandlerContext>(4);
discardingSink是用來處理上行到最上,或者下行到最下時的對消息事件的處理,這個可以不用太關注

主要來關注下head、tail、name2ctx,head和tail這一看就是鏈表的頭和尾,可以進一步猜想pipeline中的handlers是按照一個鏈表排列的。name2ctx用一個map結構提供名稱到handler的映射關係(通過handlerContext可以找到相應的handler)。

下面以addLast爲例來進行解析
public synchronized void addLast(String name, ChannelHandler handler) {
        if (name2ctx.isEmpty()) {
            init(name, handler);
        } else {
            checkDuplicateName(name);
            DefaultChannelHandlerContext oldTail = tail;
            DefaultChannelHandlerContext newTail = new DefaultChannelHandlerContext(oldTail, null, name, handler);

            callBeforeAdd(newTail);

            oldTail.next = newTail;
            tail = newTail;
            name2ctx.put(name, newTail);

            callAfterAdd(newTail);
        }
    }
依賴DefaultChannelHandlerContext來實現隊列結構,在DefaultChannelHandlerContext中有指向前一個和後一個的引用,就不細看,只用看一個DefaultChannelHandlerContext的成員變量即可。
private final class DefaultChannelHandlerContext implements ChannelHandlerContext {
        volatile DefaultChannelHandlerContext next;
        volatile DefaultChannelHandlerContext prev;
        private final String name;
        private final ChannelHandler handler;
        private final boolean canHandleUpstream;
        private final boolean canHandleDownstream;
        private volatile Object attachment;
發佈了54 篇原創文章 · 獲贊 14 · 訪問量 23萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章