Netty之Pipeline的原理和事件傳播機制

Netty之Pipeline的原理和事件傳播機制

一、包含知識點

  • ChannelPipeline初始化流程
  • ChannelPipeline初始化流程
  • ChannelInitializer添加
  • 自定義ChannelHandler添加入隊列
  • Pipeline事件傳播機制

二、 Channel和Pipeline之間關係

​ 本篇文章主要講解Netty中Pipeline相關的知識點 , 而pipeline和Channel有着緊密的聯繫, 數據的讀取、寫入都需要經過Channel,而Pipeline被綁定到了Channel, 那麼從哪裏可以知道Channel和Pipeline之間有關係呢 ?

​ 常見的Channel有NioServerSocketChannel、NioSocketChannel, 這裏我們看下NioServerSocketChannel類繼承關係, 從類繼承關係 NioServerSocketChannel -> AbstractNioMessageChannel -> AbstractNioChannel -> AbstractChannel 進行查找, 在AbstractChannel抽象類中我們找到了pipeline變量, 這說明了Channel和Pipeline是有相互關係的

//AbstractChannel
private final DefaultChannelPipeline pipeline;

在這裏插入圖片描述

三、ChannelPipeline初始化流程

3.1 Channel初始化流程

​ 在第二節中我們知道Channel和Pipeline之間存在聯繫, 那麼Pipeline的初始化是否和Channel初始化有關係呢?帶着這個疑問, 先查看抽象類AbstractChannel, 在構造方法中看到下面的代碼, pipeline是在構造函數中創建的, 而構造函數在初始化的時候纔會調用, 也就是Channel初始化的時候會同時創建pipeline對象。

//AbstractChannel
protected AbstractChannel(Channel parent) {
  this.parent = parent;
  id = newId();
  unsafe = newUnsafe();
  pipeline = newChannelPipeline();
}

protected AbstractChannel(Channel parent, ChannelId id) {
  this.parent = parent;
  this.id = id;
  unsafe = newUnsafe();
  pipeline = newChannelPipeline();
}

​ 在上篇 Netty 核心原理之運行機制 文章中我們提到了Channel創建方式,服務啓動(bind、connect)的時候會執行initAndRegister()方法, 該方法會執行channelFactory.newChannel()創建具體的Channel, 這裏以NioServerSocketChannel爲例說明

final ChannelFuture initAndRegister() {
  Channel channel = null;
  try {
    /**
     * Channel對象的創建, 來自下面語句
     * channel(NioServerSocketChannel.class)
     */
    channel = channelFactory.newChannel(); // 創建Channel對象, 這裏是 NioServerSocketChannel
    init(channel); // 對Channel對象進行初始化
  } catch (Throwable t) {
    //... 省略部分其它代碼
  }

  //... 省略部分其它代碼
  
  return regFuture;
}

​ Channel構造方法初始化調用流程如下, 從構造函數初始化流程在AbstractChannel抽象類中找到了pipeline創建邏輯, 和本小節開始時分析是一致的。

//NioServerSocketChannel
public NioServerSocketChannel() {
  this(newSocket(DEFAULT_SELECTOR_PROVIDER));
}

public NioServerSocketChannel(ServerSocketChannel channel) {
  super(null, channel, SelectionKey.OP_ACCEPT);
  config = new NioServerSocketChannelConfig(this, javaChannel().socket());
}
// AbstractNioMessageChannel
protected AbstractNioMessageChannel(Channel parent, SelectableChannel ch, int readInterestOp) {
  super(parent, ch, readInterestOp);
}

// AbstractNioChannel
protected AbstractNioChannel(Channel parent, SelectableChannel ch, int readInterestOp) {
  super(parent);
  //...省略其它部分代碼
}

//AbstractChannel
protected AbstractChannel(Channel parent) {
  this.parent = parent;
  id = newId();
  unsafe = newUnsafe();
  pipeline = newChannelPipeline();
}

3.2 Pipeline實例化

​ pipeline實例化是調用內部方法newChannelPipeline創建的

protected DefaultChannelPipeline newChannelPipeline() {
  return new DefaultChannelPipeline(this);
}

​ DefaultChannelPipeline創建的時候做了什麼事情呢? 看下下面的代碼

protected DefaultChannelPipeline(Channel channel) {
  this.channel = ObjectUtil.checkNotNull(channel, "channel");
  succeededFuture = new SucceededChannelFuture(channel, null);
  voidPromise =  new VoidChannelPromise(channel, true);

  tail = new TailContext(this);
  head = new HeadContext(this);

  head.next = tail;
  tail.prev = head;
}

​ 可以看到, 入參channel賦值給了this.channel, 同時創建了兩個字段tail、head,這兩個字段維護了以AbstractChannelHandlerContext爲節點的雙向鏈表, 分別表示隊尾、隊頭,這個雙向鏈表是Netty實現Pipeline機制的關鍵, 下面分別看下HeadContext、TailContext類結構圖

在這裏插入圖片描述

圖一、HeadContext類結構圖

在這裏插入圖片描述

圖二、TailContext類結構圖

​ 從類結構圖可以看出, HeadContext、TailContext主要相似和區別是

  • 差異
    • HeadContext實現了ChannelOutboundHandler(雖然也實現了ChannelInboundHandler)
    • TailContext實現了ChannelInboundHandler
  • 相似
    • 都實現了ChannelHandler、ChannelHandlerContext, 可以說head、tail既是一個ChannelHandler, 也是ChannelHandlerContext

​ 從上面類繼承關係我們知道HeadContext、TailContext的差異是ChannelOutboundHandler、ChannelInboundHandler, 那從哪裏可以直觀的這種區別呢 ? 我們看下HeadContext、TailContext構造方法

//HeadContext
HeadContext(DefaultChannelPipeline pipeline) {
  super(pipeline, null, HEAD_NAME, false, true);
  unsafe = pipeline.channel().unsafe();
  setAddComplete();
}

//TailContext
TailContext(DefaultChannelPipeline pipeline) {
  super(pipeline, null, TAIL_NAME, true, false);
  setAddComplete();
}

​ 從構造方法中, 主要差別是super調用時入參的區別

  • name的區別, 這點事顯然的爲了區別隊列的頭和尾
  • inbound、outbound的區別
    • head: inbound – false ,outbound – true
    • tail: inbound – true ,outbound – false

3.3 Pipeline實例化後圖示

在這裏插入圖片描述

圖三、DefaultChannelPipeline初始化時隊列結構

四、ChannelInitializer添加

4.1 init添加自定義handler、childHandler

​ 首先我們看下ChannelInitializer的使用代碼示例

ServerBootstrap b = new ServerBootstrap();
b.group(bossGroup, workerGroup)
  .channel(NioServerSocketChannel.class)
  .handler(new new LengthFieldBasedFrameDecoder())
  .childHandler(new ChannelInitializer<SocketChannel>() {

    @Override  
    protected void initChannel(SocketChannel ch) throws Exception {  
      ChannelPipeline pipeline = ch.pipeline();
      
      //對象參數類型編碼器
      pipeline.addLast("encoder",new ObjectEncoder());
      //對象參數類型解碼器
      pipeline.addLast("decoder",new ObjectDecoder(Integer.MAX_VALUE,ClassResolvers.cacheDisabled(null)));
    }  
  })
  .option(ChannelOption.SO_BACKLOG, 128)       
  .childOption(ChannelOption.SO_KEEPALIVE, true);

​ 在第三節中,DefaultChannelPipeline創建後, 並不能實現什麼功能, 因爲並沒有給他添加自定義的ChannelHandler, 而自定義功能是在childHandler或handler裏面, 通過自定義實現的ChannelInitializer#initChannel方法添加自定義handler, 那麼ChannelInitializer是什麼時候添加到ChannelPipeline的呢?在3.1節中,我們提到了initAndRegister()方法, 查看對應代碼, 在創建Channel之後還有init()方法,查看該方法代碼

//ServerBootstrap
void init(Channel channel) throws Exception {
  // ...省略部分其它代碼
  ChannelPipeline p = channel.pipeline();
  // ...
  p.addLast(new ChannelInitializer<Channel>() {
    @Override
    public void initChannel(Channel ch) throws Exception {
      final ChannelPipeline pipeline = ch.pipeline();
      ChannelHandler handler = config.handler(); //初始化時 .handler() 對應邏輯
      if (handler != null) {
        pipeline.addLast(handler);
      }
			
      ch.eventLoop().execute(new Runnable() { //初始化時 .childHandler() 對應邏輯
                    @Override
                    public void run() {
                        pipeline.addLast(new ServerBootstrapAcceptor(
                                currentChildGroup, currentChildHandler, currentChildOptions, currentChildAttrs));
                    }
                });
      //... 省略部分其它代碼
    }
  });
}

​ init方法初始化相關信息的時候, 通過ChannelInitializer將handler、chiledHandler中添加的自定義handler添加到pipeline隊列中(這裏是以Server端init方法舉例),從源碼中可以看出存在handler時纔會進行添加,而childHandler會被封裝成ServerBootstrapAcceptor再進行添加,如果是Client則直接對handler進行添加, 那麼handler、childHandler有什麼區別呢 ?

  • handler:
    • 在Server、Client端都存在對應實現
    • 初始化的時候,如果handler不爲空, 會執行相關handler
    • 用於監聽Channel各種動作以及狀態的改變, 包括連接、綁定、接收消息等
  • childHandler
    • 只在Server端存在
    • 在客戶端成功連接後才執行, 源碼中chidlHandler是在自定義線程中被封裝成ServerBootstrapAcceptor再添加到pipeline
    • 用於監聽已連接客戶端的Channel動作和狀態

4.2 handler轉ChannelHandlerContext

​ 在上一節中,我們知道通過addLast方法添加的參數是ChannelHandler, 而隊列維護的元素是AbstractChannelHandlerContext, 那麼ChannelHandler是怎麼轉換爲AbstractChannelHandlerContext的呢 ?查看addLast()核心方法

public final ChannelPipeline addLast(EventExecutorGroup group, String name, ChannelHandler handler) {
  final AbstractChannelHandlerContext newCtx;
  synchronized (this) {
    checkMultiplicity(handler); // 檢查handler是否重複
    newCtx = newContext(group, filterName(name, handler), handler); // 創建DefaultChannelHandlerContext
    addLast0(newCtx); //將Context添加到隊列尾部
  }
  // 省略部分其它代碼
  
  return this;
}

​ 該構造方法主要做了下面幾個事情

  • 爲了避免併發問題, 用synchronized同步鎖進行修飾
  • checkMultiplicity()方法檢測handler是否重複, 如果重複會拋ChannelPipelineException異常
  • newContext()方法將handler封裝成AbstractChannelHandlerContext
  • addLast0()方法將新創建的newCtx添加到隊列中

newContxt()方法具體幹什麼了呢 ? 查詢代碼

//DefaultChannelPipeline
private AbstractChannelHandlerContext newContext(EventExecutorGroup group, String name, ChannelHandler handler) {
  return new DefaultChannelHandlerContext(this, childExecutor(group), name, handler);
}

//DefaultChannelHandlerContext
DefaultChannelHandlerContext(
            DefaultChannelPipeline pipeline, EventExecutor executor, String name, ChannelHandler handler) {
  super(pipeline, executor, name, isInbound(handler), isOutbound(handler));
  if (handler == null) {
    throw new NullPointerException("handler");
  }
  this.handler = handler;
}

​ 在構造方法DefaultChannelHandlerContext調用super操作時, 調用了isInbound、isOutnound方法,下面是兩個方法的實現邏輯

private static boolean isInbound(ChannelHandler handler) {
  return handler instanceof ChannelInboundHandler;
}

private static boolean isOutbound(ChannelHandler handler) {
  return handler instanceof ChannelOutboundHandler;
}

​ 從實現邏輯可以看出, isInbound、isOutbound分別是以ChannelInboundHandler、ChannelOutboundHandler來確定的, 如果handler繼承了ChannelInboundHandler則爲inbound、如果繼承了ChannelOutboundHandler則爲outBound

  • ChannelInboundHandler
    • ZlibDecoder
    • LineBasedFrameDecoder
    • ObjectDecoder
  • ChannelOutboundHandler
    • Bzip2Encoder
    • ObjectEncoder
    • LengthFieldPrepender

​ 通過newCtx方法創建完Context後, 執行addLast0方法進行入隊操作, 下面是相關邏輯代碼,在保持HeadContext、TailContext隊頭、隊尾不變的情況下, 將新添加的節點newCtx作爲隊列的尾部。

private void addLast0(AbstractChannelHandlerContext newCtx) {
  AbstractChannelHandlerContext prev = tail.prev;
  newCtx.prev = prev;
  newCtx.next = tail;
  prev.next = newCtx;
  tail.prev = newCtx;
}

4.3 init之後pipeline隊列情況

在這裏插入圖片描述

五、自定義ChannelHandler添加入隊列

5.1 自定義ChannelHandler添加流程

​ 第四節中我們分析了child、childHandler中ChannelInitiallzer添加入隊列的流程, ChannelInitiallzer中定義的自定義ChannelHandler又是怎麼添加到隊列中的呢 ?本小節對這個做分析,首先我們再次看下initAndRegister方法

final ChannelFuture initAndRegister() {
  Channel channel = null;
  try {
    channel = channelFactory.newChannel(); //創建channel
    init(channel); // 初始化channel
  } catch (Throwable t) {
    // 省略部分代碼
  }

  ChannelFuture regFuture = config().group().register(channel); // 將channel進行註冊到selector
  //省略部分代碼
  
  return regFuture;
}

​ 前面我們已經分析過newChannel()、init()相關方法對應邏輯,只有handler、childHandler對應ChannelInitializer添加涉及到pipeline邏輯,沒有涉及到自定義ChannelHandler添加入隊列邏輯, 繼續查看initAndRegister方法, 其中有下面部分代碼

ChannelFuture regFuture = config().group().register(channel); 

​ 這部分代碼在 Netty 核心原理之運行機制 已經分析過, 這裏不再分析,直接跟蹤代碼到如下register0代碼,代碼塊中,只保留了需要分析的部分邏輯代碼

//AbstractChannel
private void register0(ChannelPromise promise) {
  try {
    // 省略部分代碼
    pipeline.fireChannelRegistered();
    // 省略部分代碼
  } catch (Throwable t) {
    // 省略部分代碼
  }
}

//DefaultChannelPipeline
public final ChannelPipeline fireChannelRegistered() {
  AbstractChannelHandlerContext.invokeChannelRegistered(head);
  return this;
}

//AbstractChannelHandlerContext
static void invokeChannelRegistered(final AbstractChannelHandlerContext next) {
  EventExecutor executor = next.executor();
  if (executor.inEventLoop()) {
    next.invokeChannelRegistered();
  } else {
    executor.execute(new Runnable() {
      @Override
      public void run() {
        next.invokeChannelRegistered();
      }
    });
  }
}

//AbstractChannelHandlerContext
private void invokeChannelRegistered() {
  if (invokeHandler()) {
    try {
      ((ChannelInboundHandler) handler()).channelRegistered(this);
    } catch (Throwable t) {
      notifyHandlerException(t);
    }
  } else {
    fireChannelRegistered();
  }
}

// AbstractChannelHandlerContext
private AbstractChannelHandlerContext findContextInbound() {
  AbstractChannelHandlerContext ctx = this;
  do {
    ctx = ctx.next;
  } while (!ctx.inbound);
  return ctx;
}

​ 從代碼中可以看到, 調用靜態方法invokeChannelRegistered時直接將head作爲了入參, 顯然會從head開始遍歷Pipeline雙向鏈表, 找到屬性爲inbound的ChannelHandler, 然後處理對應邏輯, 下面是邏輯調用流程, 其中紅色部分是循環處理邏輯,持續尋找屬性爲inBound的ChannelHandler

AbstractChannel#register0(promise) -> DefaultChannelPipeline#fireChannelRegistered() -> AbstractChannelHandlerContext#invokeChannelRegistered(head) -> **AbstractChannelHandlerContext#invokeChannelRegistered() -> AbstractChannelHandlerContext#fireChannelRegistered() -> AbstractChannelHandlerContext#findContextInbound() **

​ 如果當前inBound是ADD_COMPLETE操作 或 ADD_PENDING且order=false,會調用channelRegistered操作, 初始化之後由4.3 圖示, ChannelHandler就是ChannelInitializer, 實際調用的是ChannelInitializer的channelRegistered方法

//ChannelInitializer
public final void channelRegistered(ChannelHandlerContext ctx) throws Exception {
  if (initChannel(ctx)) {
    ctx.pipeline().fireChannelRegistered();
  } else {
    ctx.fireChannelRegistered();
  }
}

private boolean initChannel(ChannelHandlerContext ctx) throws Exception {
  if (initMap.putIfAbsent(ctx, Boolean.TRUE) == null) { // Guard against re-entrance.
    try {
      initChannel((C) ctx.channel());
    } catch (Throwable cause) {
      // Explicitly call exceptionCaught(...) as we removed the handler before calling initChannel(...).
      // We do so to prevent multiple calls to initChannel(...).
      exceptionCaught(ctx, cause);
    } finally {
      remove(ctx);
    }
    return true;
  }
  return false;
}

​ 在執行channelRegistered方法時,會執行方法initChannel()進行自定義ChannelHandler添加操作, 執行initChannel方法時,會調用Bootstrap、ServerBootstrap初始化時ChannelInitializer重寫的initChannel(Channel)方法, 即下面邏輯代碼

childHandler(new ChannelInitializer<SocketChannel>() {
  @Override  
  protected void initChannel(SocketChannel ch) throws Exception {  
    ChannelPipeline pipeline = ch.pipeline();
    pipeline.addLast(new LengthFieldBasedFrameDecoder(Integer.MAX_VALUE, 0, 4, 0, 4));
    //自定義協議編碼器
    pipeline.addLast(new LengthFieldPrepender(4));
    //對象參數類型編碼器
    pipeline.addLast("encoder",new ObjectEncoder());
  }  
})

​ 添加之後會執行finally邏輯代碼塊, 該邏輯主要是將init時添加的ChannelHandler, 即ChannelInitializer進行刪除

//ChannelInitializer
private void remove(ChannelHandlerContext ctx) {
  try {
    ChannelPipeline pipeline = ctx.pipeline();
    if (pipeline.context(this) != null) {
      pipeline.remove(this);
    }
  } finally {
    initMap.remove(ctx);
  }
}

5.2 自定義ChannelHandler入隊後情況

在這裏插入圖片描述

5.3 ChannelHandler默認命名規則

​ 在執行addLast()進行ChannelHandler添加時, 如果沒有指定handler名稱,會執行filterName()方法創建名稱, 下面是具體的代碼, 從代碼的執行邏輯可以看見, ChannelHandler名字規則是SimpleName + “”#0", 比如: ObjectEncoder -> ObjectEncoder#0

//DefaultChannelPipeline
private String filterName(String name, ChannelHandler handler) {
  if (name == null) {
    return generateName(handler);
  }
  checkDuplicateName(name);
  return name;
}

private String generateName(ChannelHandler handler) {
  Map<Class<?>, String> cache = nameCaches.get();
  Class<?> handlerType = handler.getClass();
  String name = cache.get(handlerType);
  if (name == null) {
    name = generateName0(handlerType);
    cache.put(handlerType, name);
  }

  // It's not very likely for a user to put more than one handler of the same type, but make sure to avoid
  // any name conflicts.  Note that we don't cache the names generated here.
  if (context0(name) != null) {
    String baseName = name.substring(0, name.length() - 1); // Strip the trailing '0'.
    for (int i = 1;; i ++) {
      String newName = baseName + i;
      if (context0(newName) == null) {
        name = newName;
        break;
      }
    }
  }
  return name;
}

private static String generateName0(Class<?> handlerType) {
  return StringUtil.simpleClassName(handlerType) + "#0";
}

六、Pipeline事件傳播機制

6.1 事件傳播機制

​ 在類ChannelPipeline中有下面對事件傳播介紹, 在AbstractChannelHandlerContext中有inbound、outbound兩個boolean變量, 用於標識handler類型

  • inbound=true, 表示其對應的ChannelHandler是ChannelInboundHandler子類
  • outbound=true,表示其對應的ChannelHandler是ChannelOutboundHandler子類

在這裏插入圖片描述

​ 從事件傳播流程可以看出, inbound、outbound事件流向是不同的

  • inbound
    • 事件流向是自低向上
    • 通過 ChannelHandlerContext.fireIN_EVT() 方法進行傳遞
  • outbound
    • 事件流向是自頂向下
    • 通過 ChannelHandlerContext.OUT_EVT() 方法進行傳遞

6.2 事件傳播分類

​ 事件傳播分爲inbound、outbound,它們分別對應ChannelInboundHandler、ChannelOutboundHandler, 查閱接口, 包含的事件傳播方法包含

  • inbound, 事件回調, 響應請求事件
public interface ChannelInboundHandler extends ChannelHandler {
    void channelRegistered(ChannelHandlerContext ctx) throws Exception;
    void channelUnregistered(ChannelHandlerContext ctx) throws Exception;
    void channelActive(ChannelHandlerContext ctx) throws Exception;
    void channelInactive(ChannelHandlerContext ctx) throws Exception;
    void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception;
    void channelReadComplete(ChannelHandlerContext ctx) throws Exception;
    void userEventTriggered(ChannelHandlerContext ctx, Object evt) throws Exception;
    void channelWritabilityChanged(ChannelHandlerContext ctx) throws Exception;
    void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception;
}
  • outbound, 主動觸發, 發起請求事件
public interface ChannelOutboundHandler extends ChannelHandler {
    void bind(ChannelHandlerContext ctx, SocketAddress localAddress, ChannelPromise promise) throws Exception;
    void connect(
            ChannelHandlerContext ctx, SocketAddress remoteAddress,
            SocketAddress localAddress, ChannelPromise promise) throws Exception;
    void disconnect(ChannelHandlerContext ctx, ChannelPromise promise) throws Exception;
    void close(ChannelHandlerContext ctx, ChannelPromise promise) throws Exception;
    void deregister(ChannelHandlerContext ctx, ChannelPromise promise) throws Exception;
    void read(ChannelHandlerContext ctx) throws Exception;
    void write(ChannelHandlerContext ctx, Object msg, ChannelPromise promise) throws Exception;
    void flush(ChannelHandlerContext ctx) throws Exception;
}

6.3 自定義事件傳播方式

​ 那麼對於捕獲的事件, 如果需要將這個事件傳遞下去, 需要調用 fireChannelActive() 方法

//自己定義的 MyInBoundHandler
public class MyInBoundHandler extends ChannelInboundhandlerAdapter{
  public void channelActive(ChannelHandlerContext ctx) {
    //業務邏輯處理
    ctx.fireChannelActive();
  }
}
//AbstractChannelHandlerContext
public ChannelHandlerContext fireChannelActive() {
  final AbstractChannelHandlerContext next = findContextInbound();
  invokeChannelActive(next);
  return this;
}

​ MyInBoundHandler處理完邏輯後, 會調用fireChannelActive將邏輯傳遞下去, findContextInbound會進行遍歷找到下一個InboundHandler繼續進行邏輯處理

6.4 OutBound事件傳播方式

​ 在6.2節中,我們提到OutBound屬於主動觸發, 發起請求事件, 這裏以Bootstrap的connect爲例進行說明, 跟蹤connect代碼, 核心處理邏輯如下代碼

//AbstractChannel
public ChannelFuture connect(SocketAddress remoteAddress, SocketAddress localAddress, ChannelPromise promise) {
  return pipeline.connect(remoteAddress, localAddress, promise);
}

//DefaultChannelPipeline
public final ChannelFuture connect(
  SocketAddress remoteAddress, SocketAddress localAddress, ChannelPromise promise) {
  return tail.connect(remoteAddress, localAddress, promise);
}

//AbstractChannelHandlerContext
public ChannelFuture connect(
            final SocketAddress remoteAddress, final SocketAddress localAddress, final ChannelPromise promise) {

  if (remoteAddress == null) {
    throw new NullPointerException("remoteAddress");
  }
  if (!validatePromise(promise, false)) {
    // cancelled
    return promise;
  }

  final AbstractChannelHandlerContext next = findContextOutbound();
  EventExecutor executor = next.executor();
  if (executor.inEventLoop()) {
    next.invokeConnect(remoteAddress, localAddress, promise);
  } else {
    safeExecute(executor, new Runnable() {
      @Override
      public void run() {
        next.invokeConnect(remoteAddress, localAddress, promise);
      }
    }, promise, null);
  }
  return promise;
}

//AbstractChannelHandlerContext
private AbstractChannelHandlerContext findContextOutbound() {
  AbstractChannelHandlerContext ctx = this;
  do {
    ctx = ctx.prev;
  } while (!ctx.outbound);
  return ctx;
}

​ 從代碼可以看出, outBound事件傳播是從隊列尾部開始的, 在AbstractChannelHandlerContext#connect中, 核心方法 findContextOutbound()循環查找隊列中outbound=true的ChannelHandler, 找到後通過invokeConnect進行連接操作, 繼續看invokeConnect()代碼

//AbstractChannelHandlerContext
private void invokeConnect(SocketAddress remoteAddress, SocketAddress localAddress, ChannelPromise promise) {
  if (invokeHandler()) {
    try {
      ((ChannelOutboundHandler) handler()).connect(this, remoteAddress, localAddress, promise);
    } catch (Throwable t) {
      notifyOutboundHandlerException(t, promise);
    }
  } else {
    connect(remoteAddress, localAddress, promise);
  }
}

//ChannelOutboundHandlerAdatper
public void connect(ChannelHandlerContext ctx, SocketAddress remoteAddress,
                    SocketAddress localAddress, ChannelPromise promise) throws Exception {
  ctx.connect(remoteAddress, localAddress, promise);
}

​ 如果業務邏輯代碼沒有重寫connect方法, 會調用ChannelOutboundHandlerAdatper的connect()進行連接操作, 而這個方法僅僅調用了ctx.connect(),之後這個調用將會 進入下面調用循環, 注意這裏ChannelHandlerContext 是AbstractChannelHandlerContext

ChannelHandlerContext.connect() -> ChannelHandlerContext.findContextOutbound() -> ChannelHandlerContext.invokeConnect() -> ChannelOutboundHandlerAdatper.connect() -> ChannelHandlerContext.connect()

​ 直到connect事件傳遞到DefaultChannelPipeline雙向隊列的頭節點, handler爲HeadContext, 會直接返回當前對象this, 最終connect鏈接事件將會在head中被處理, 下面是相關邏輯代碼, 到達head後整個全球事件也就結束了

//HeadContext
public ChannelHandler handler() {
  return this;
}

//HeadContext
public void connect(
  ChannelHandlerContext ctx,
  SocketAddress remoteAddress, SocketAddress localAddress,
  ChannelPromise promise) throws Exception {
  unsafe.connect(remoteAddress, localAddress, promise);
}

6.5 inBound事件傳播方式

​ 從6.1事件傳播流程,大致可以看出inBound和outBound處理事件過程相似, 只是傳播方向不一樣, 在6.2節中提到inbound是事件回調, 響應請求事件

​ 這裏我們接着6.4節繼續分析,connect進行連接之後,肯定需要進行回調操作,而Client連接Server是在AbstractNioChannel中,查看下面代碼

//AbstractNioChannel
public final void connect(
  //省略部分其它代碼
  try {
    boolean wasActive = isActive();
    if (doConnect(remoteAddress, localAddress)) { // 執行鏈接操作
      fulfillConnectPromise(promise, wasActive); // 鏈接成功,執行事件回調操作
    } else {
      //省略部分其它代碼
    }
  } catch (Throwable t) {
    promise.tryFailure(annotateConnectException(t, remoteAddress));
    closeIfClosed();
  }
}
  
//AbstractNioChannel
private void fulfillConnectPromise(ChannelPromise promise, boolean wasActive) {
  // 省略部分其它代碼
  if (!wasActive && active) {
    pipeline().fireChannelActive();
  }
	if (!promiseSet) {
    close(voidPromise());
  }
}

​ 在fulfillConnectPromise()方法中,執行DefaultChannelPipeline的fireChannelActive()方法進行回調操作, 這個應該比較熟悉, 這個方法在前面已經分析過, 以head作爲起始遍歷節點, 這裏我們只關注下面的邏輯

//AbstractChannelHandlerContext
private void invokeChannelActive() {
  if (invokeHandler()) {
    try {
      ((ChannelInboundHandler) handler()).channelActive(this);
    } catch (Throwable t) {
      notifyHandlerException(t);
    }
  } else {
    fireChannelActive();
  }
}

//ChannelInboundHandlerAdapter
public void channelActive(ChannelHandlerContext ctx) throws Exception {
  ctx.fireChannelActive();
}

​ 可以看出之後這裏和outBound調用循環很相似, inbound會進入下面的循環, 注意這裏ChannelHandlerContext 是AbstractChannelHandlerContext

ChannelHandlerContext.fireChannelActive() -> ChannelHandlerContext.findContextInbound -> ChannelHandlerContext.invokeChannelActive() -> ChannelInboundHandlerAdapter.channelActive() -> ChannelHandlerContext.fireChannelActive()

​ 當消息傳遞到TailContext後, 會執行TailContext的channelActive()方法,但是該方法空實現, 默認是不處理。

public void channelActive(ChannelHandlerContext ctx) throws Exception { }

6.6 事件傳播總結

  • Inbound

    • Inbound事件是通知型事件, 當某件事件就緒後會自底向上進行通知
    • Inbound事件的發起者是unsafe
    • inbound事件的處理者是Channel,如果用戶沒有實現自定義處理方法, inbound事件默認的處理者是TailContext(實際是空實現)
    • Inbound事件在雙向鏈表中的傳播方向是head -> tail
    • ChannelHandler在處理事件時, 如果這個handler不是最後一個handler,會調用ChannelHandlerContext.fireIN_EVT()方法, 將事件傳播下去, 比如上面提到的fireChannelActive(),如果不這麼做事件傳播會終止
  • outBound

    • outbound事件是請求型事件
    • outbound事件的處理者是unsafe
    • outbound事件的發起者是Channel
    • outBound事件在雙向鏈表中的傳播方向是 tail -> head
    • ChannelHandler在處理事件時, 如果這個handler 不是最後一個handler, 會調用ChannelHandlerContext.OUT_EVT()方法, 將事件傳播下去, 比如上面提到的ctx.connect(), 如果不這麼做事件傳播會終止
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章