【Netty】(8)---理解ChannelPipeline

ChannelPipeline

     ChannelPipeline不是單獨存在,它確定會和Channel、ChannelHandler、ChannelHandlerContext關聯在一塊兒,因此有關概念這裏一塊兒講。html

1、ChannelHandler

  一、概念

 先看圖java

ChannelHandler下主要是兩個子接口設計模式

      ChannelInboundHandler(入站): 處理輸入數據和Channel狀態類型改變。promise

                                      適配器: ChannelInboundHandlerAdapter(適配器設計模式)異步

                                      經常使用的: SimpleChannelInboundHandleride

    ChannelOutboundHandler(出站): 處理輸出數據函數

                                      適配器: ChannelOutboundHandlerAdapteroop

每個Handler都必定會處理出站或者入站(可能二者都處理數據),例如對於入站的Handler可能會繼承SimpleChannelInboundHandler或者ChannelInboundHandlerAdapter,學習

而SimpleChannelInboundHandler又是繼承於ChannelInboundHandlerAdapter,最大的區別在於SimpleChannelInboundHandler會對沒有外界引用的資源進行必定的清理,this

而且入站的消息能夠經過泛型來規定。

這裏爲何有設配器模式呢?

      咱們在寫自定義Handel時候,不多會直接實現上面兩個接口,由於接口中有不少默認方法須要實現,因此這裏就採用了設配器模式,ChannelInboundHandlerAdapter和

ChannelInboundHandlerAdapter就是設配器模式的產物,讓它去實現上面接口,實現它全部方法。那麼你本身寫自定義Handel時,只要繼承它,就無須重寫上面接口的全部方法了。

 二、Channel 生命週期(執行順序也是從上倒下)

   (1)channelRegistered: channel註冊到一個EventLoop。

   (2)channelActive: 變爲活躍狀態(鏈接到了遠程主機),能夠接受和發送數據

   (3)channelInactive: channel處於非活躍狀態,沒有鏈接到遠程主機

   (4)channelUnregistered: channel已經建立,可是未註冊到一個EventLoop裏面,也就是沒有和Selector綁定

 三、ChannelHandler 生命週期

      handlerAdded: 當 ChannelHandler 添加到 ChannelPipeline 調用

  handlerRemoved: 當 ChannelHandler 從 ChannelPipeline 移除時調用

 exceptionCaught: 當 ChannelPipeline 執行拋出異常時調用

 

2、ChannelPipeline

  一、概念

先看圖

       ChannelPipeline類是ChannelHandler實例對象的鏈表,用於處理或截獲通道的接收和發送數據。它提供了一種高級的截取過濾模式(相似serverlet中的filter功能),讓用

戶能夠在ChannelPipeline中徹底控制一個事件以及如何處理ChannelHandler與ChannelPipeline的交互。

       對於每一個新的通道Channel,都會建立一個新的ChannelPipeline,並將器pipeline附加到channel中。

下圖描述ChannelHandler與pipeline中的關係,一個io操做能夠由一個ChannelInboundHandler或ChannelOutboundHandle進行處理,並經過調用ChannelInboundHandler

處理入站io或經過ChannelOutboundHandler處理出站IO。

二、經常使用方法

      addFirst(...)   //添加ChannelHandler在ChannelPipeline的第一個位置
     addBefore(...)   //在ChannelPipeline中指定的ChannelHandler名稱以前添加ChannelHandler
      addAfter(...)   //在ChannelPipeline中指定的ChannelHandler名稱以後添加ChannelHandler
       addLast(...)   //在ChannelPipeline的末尾添加ChannelHandler
        remove(...)   //刪除ChannelPipeline中指定的ChannelHandler
       replace(...)   //替換ChannelPipeline中指定的ChannelHandler

ChannelPipeline能夠動態添加、刪除、替換其中的ChannelHandler,這樣的機制能夠提升靈活性。示例:

1.    ChannelPipeline pipeline = ch.pipeline(); 
2.    FirstHandler firstHandler = new FirstHandler(); 
3.    pipeline.addLast("handler1", firstHandler); 
4.    pipeline.addFirst("handler2", new SecondHandler()); 
5.    pipeline.addLast("handler3", new ThirdHandler()); 
6.    pipeline.remove("「handler3「"); 
7.    pipeline.remove(firstHandler); 
8.    pipeline.replace("handler2", "handler4", new FourthHandler());<span style="font-family:Microsoft YaHei;font-size:14px;">

 三、入站出站Handler執行順序

  通常的項目中,inboundHandler和outboundHandler有多個,在Pipeline中的執行順序?

   重點記住: InboundHandler順序執行OutboundHandler逆序執行

   問題: 下面的handel的執行順序?

          ch.pipeline().addLast(new InboundHandler1());
          ch.pipeline().addLast(new OutboundHandler1());
          ch.pipeline().addLast(new OutboundHandler2());
          ch.pipeline().addLast(new InboundHandler2());
  或者:
          ch.pipeline().addLast(new OutboundHandler1());
          ch.pipeline().addLast(new OutboundHandler2());
          ch.pipeline().addLast(new InboundHandler1());
          ch.pipeline().addLast(new InboundHandler2());

其實上面的執行順序都是同樣的:

 InboundHandler1--> InboundHandler2 -->OutboundHandler2 -->OutboundHandler1

結論

      1)InboundHandler順序執行,OutboundHandler逆序執行

      2)InboundHandler之間傳遞數據,經過ctx.fireChannelRead(msg)

      3)InboundHandler經過ctx.write(msg),則會傳遞到outboundHandler

      4)  使用ctx.write(msg)傳遞消息,Inbound須要放在結尾,在Outbound以後,否則outboundhandler會不執行;

           可是使用channel.write(msg)、pipline.write(msg)狀況會不一致,都會執行,那是由於channel和pipline會貫穿整個流。

      5)  outBound和Inbound誰先執行,針對客戶端和服務端而言,客戶端是發起請求再接受數據,先outbound再inbound,服務端則相反

 

3、ChannelHandlerContext

    ChannelPipeline並非直接管理ChannelHandler,而是經過ChannelHandlerContext來間接管理,這一點經過ChannelPipeline的默認實現DefaultChannelPipeline能夠看出來。

    DefaultChannelHandlerContext和DefaultChannelPipeline是ChannelHandlerContext和ChannelPipeline的默認實如今DefaultPipeline內部

DefaultChannelHandlerContext組成了一個雙向鏈表。 咱們看下DefaultChannelPipeline的構造函數:

/**
  * 能夠看到,DefaultChinnelPipeline 內部使用了兩個特殊的Hander 來表示Handel鏈的頭和尾。
  */
 public DefaultChannelPipeline(AbstractChannel channel) {
        if (channel == null) {
            throw new NullPointerException("channel");
        }
        this.channel = channel;
 
        TailHandler tailHandler = new TailHandler();
        tail = new DefaultChannelHandlerContext(this, null, generateName(tailHandler), tailHandler);
 
        HeadHandler headHandler = new HeadHandler(channel.unsafe());
        head = new DefaultChannelHandlerContext(this, null, generateName(headHandler), headHandler);
 
        head.next = tail;
        tail.prev = head;
    } 

因此對於DefaultChinnelPipeline它的Handel頭部和尾部的Handel是固定的,咱們所添加的Handel是添加在這個頭和尾以前的Handel。(下面這個圖更加清晰)

 

4、幾者關係

先大體說下什麼是Channel

一般來講, 全部的 NIO 的 I/O 操做都是從 Channel 開始的. 一個 channel 相似於一個 stream。在Netty中,Channel是客戶端和服務端創建的一個鏈接通道

雖然java Stream 和 NIO Channel都是負責I/O操做,但他們仍是有許多區別的:

      1)咱們能夠在同一個 Channel 中執行讀和寫操做, 然而同一個 Stream 僅僅支持讀或寫

     2)Channel 能夠異步地讀寫, 而 Stream 是阻塞的同步讀寫

     3)Channel 老是從 Buffer 中讀取數據, 或將數據寫入到 Buffer 中

 幾者的關係圖以下:

總結:

      一個Channel包含一個ChannelPipeline,建立Channel時會自動建立一個ChannelPipeline,每一個Channel都有一個管理它的pipeline,這關聯是永久性的。

這點從源碼中就能夠看出,我以前寫的博客裏有說到:【Netty】5 源碼 Bootstrap。每個ChannelPipeline中能夠包含多個ChannelHandler。全部ChannelHandler

都會順序加入到ChannelPipeline中,ChannelHandler實例與ChannelPipeline之間的橋樑是ChannelHandlerContext實例。

 

5、整個傳播流程

     如今將上面的整個傳播流程,經過源碼大體走一遍。

     爲了搞清楚事件如何在Pipeline裏傳播,讓咱們從Channel的抽象子類AbstractChannel開始,下面是AbstractChannel#write()方法的實現:

public abstract class AbstractChannel extends DefaultAttributeMap implements Channel {
    // ...
    @Override
    public Channel write(Object msg) {
        return pipeline.write(msg);
    }
    // ...
}

     AbstractChannel直接調用了Pipeline的write()方法:

再看DefaultChannelPipeline的write()方法實現:

final class DefaultChannelPipeline implements ChannelPipeline {
    // ...
    @Override
    public ChannelFuture write(Object msg) {
        return tail.write(msg);
    }
    // ...
}

由於write是個outbound事件,因此DefaultChannelPipeline直接找到tail部分的context,調用其write()方法:

 

接着看DefaultChannelHandlerContext的write()方法

final class DefaultChannelHandlerContext extends DefaultAttributeMap implements ChannelHandlerContext {
    // ...
    @Override
    public ChannelFuture write(Object msg) {
        return write(msg, newPromise());
    }
 
    @Override
    public ChannelFuture write(final Object msg, final ChannelPromise promise) {
        if (msg == null) {
            throw new NullPointerException("msg");
        }
 
        validatePromise(promise, true);
 
        write(msg, false, promise);
 
        return promise;
    }
 
    private void write(Object msg, boolean flush, ChannelPromise promise) {
        DefaultChannelHandlerContext next = findContextOutbound();
        next.invokeWrite(msg, promise);
        if (flush) {
            next.invokeFlush();
        }
    }
 
    private DefaultChannelHandlerContext findContextOutbound() {
        DefaultChannelHandlerContext ctx = this;
        do {
            ctx = ctx.prev;
        } while (!ctx.outbound);
        return ctx;
    }
 
    private void invokeWrite(Object msg, ChannelPromise promise) {
        try {
            ((ChannelOutboundHandler) handler).write(this, msg, promise);
        } catch (Throwable t) {
            notifyOutboundHandlerException(t, promise);
        }
    }
 
    // ...
}

context的write()方法沿着context鏈往前找,直至找到一個outbound類型的context爲止,而後調用其invokeWrite()方法

 

  invokeWrite()接着調用handler的write()方法

 

最後看看ChannelOutboundHandlerAdapter的write()方法實現:

public class ChannelOutboundHandlerAdapter extends ChannelHandlerAdapter implements ChannelOutboundHandler {
    // ...
    @Override
    public void write(ChannelHandlerContext ctx, Object msg, ChannelPromise promise) throws Exception {
        ctx.write(msg, promise);
    }
    // ...
}

 默認的實現調用了context的write()方法而不作任何處理,這樣write事件就沿着outbound鏈繼續傳播:

 

可見,Pipeline的事件傳播,是靠Pipeline,Context和Handler共同協做完成的。

 

參考 

   Netty4學習筆記-- ChannelPipeline(很是感謝做者分享,讓我對事件傳播有了更清晰的認識)

 

 

若是一我的充滿快樂,正面的思想,那麼好的人事物就會和他共鳴,並且被他吸引過來。一樣,一我的老帶悲傷,倒黴的事情也會跟過來。

                                                                                    ——在本身心情低落的時候,告誡本身不要把負能量帶給別人。(大校18) 

相關文章
相關標籤/搜索