第 6 章 ChannelHandler 相关

概述

    ChannelHandler 作为程序逻辑的核心部分,其地位是不可估量的。接下来我们将对它以及与它相关的一些组件进行详细的描述。

    首先我们看一下如下这张图,对 ChannelHandler 相关组件之间的关系有一个比较直观的认识


    从上图我们对 Channel 相关组件的关系有了一个大概的认识。接下我们对每一个进行详细说明

6.2 ChannelPipeLine

    对于每一个新创建的 Channel ,一定且只会被分配一个 ChannelPipeLine。这种一 一 对应的关系在 Channel 的生命周期内都是存在的。一个 ChannelPipeLine 由多个 ChannelHandler 组成,它就等价于一个 ChannelHandler 实例链。如下图,展示了一个典型同时具有入站和出站 ChannelHandler 的 ChannelPipeLine 布局


    在 Netty 中总是将 ChannelPipeLine 的入站口——最先读取数据的处理器(上图中的左侧:1号)作为头部,而将出站口——最先写入数据的处理器(上图中的最右侧:5号)作为尾部。

    从上图还可以得知:入站 ChannelHandler 和 出站 ChannelHandler 可以同时添加到 ChannelPipeLine 中,ChannelHandler 的排序与它们的添加次序有关。当一个入站事件在 ChannelPipeLine 中传播时,他会“自动”跳过处理出站事件的相关 ChannelHandler(也就是不相关的 Handler,如上图中的3、5);对于出站事件类似。这里有个问题

    如何实现自动跳过不想关的处理器的?

    其实如果简单从代码实现层面来理解,需要两步走:

    1. 确定该 Handler 是出站还是入站 Handler

扫描二维码关注公众号,回复: 1743866 查看本文章

     由于入站 Handler 和 出站 Handler 传输数据使用的方法不一样,那自定义 Handler(selfHandler)只需要覆盖 ChannelHandlerAdapter 中不同的方法即可判断该 selfHandler 是出站还是入站事件的 Handler。例如,若 selfHandler 覆盖了 ChannelHandlerAdapter 中 channelRead(ChannelHandlerContext ctx, Object msg) 方法,则为入站 Handler;如  selfHandler 覆盖了 ChannelHandlerAdapter 中 write(ChannelHandlerContext ctx, Object msg, ChannelPromise promise) 方法,则为出站 Handler;若 selfHandler 同时覆盖了channelRead 和 write 两个方法,则 selfHandler 是同时可以处理出、入站事件的 Handler。

    2. 通过 @Skip 来跳过对应的 Handler

   如果确定好 selfChannelHandler 是出站 Handler 的时候,当上图中的 2 号 入站 Handler 传递消息到 3 号 selfChannelHandler (出站 Handler)的时候,由于 selfChannelHandler 没有实现对应的 channelRead 方法,所以会走 ChannelHandlerAdapter 的 默认实现方法,该默认实现方法有一 注解 @Skip(跳过),顾名思义,跳过该 handler。

6.2.1 修改 ChannelPipeLine

    通过调用 ChannelPipeLine 的相关方法,我们可以灵活的控制 ChannelHandler 在ChannelPipeLine 的位置,从而可以控制业务逻辑。常用方法如下

    addFirst()、addBefore()、addAfter()、 addLast()、 remove()、replace()。

    总结说来

  •     ChannelPipeLine 保存了与 Channel 相关联的所有 ChannelHandler
  •     ChannelPipeLine 可以根据需要添加或者删除 ChannelHandler 来动态修改
  •     ChannelPipeLine 有着丰富的 API 可以被调用,以相应出、入站事件


6.3 ChannelHandlerContext

    ChannelHandlerContext 是 ChannelPipeLine 和 ChannelHandler 之间的一种关联,主要功能就是管理它所关联的 ChannelHandler 与 同一个 ChannelPipeLine 中的 其他 ChannelHandler 之间的交互。

    每当向 ChannelPipeLine 中添加 ChannelHandler 时,都会创建一个 ChannelHandlerContext 。在 ChannelPipeLine、Channel、ChannelHandlerContext 三个勒种接口中有些共同的方法。三个不同的接口调用同样一个方法来处理事件的时候,会有所不同:ChannelPipeLine、和Channel调用的处理事件的方法,该事件会沿着整个 ChannelPipeLine 传播,ChannelHandlerContext 调用同样的方法的时候,事件从下一个能处理该事件的 ChannelHandler 开始传播的。

    针对 ChannelHandlerContext ,有以下两大特性:

    ChannelHandlerContext 的方法会产生更短的事件流,应该尽可能的利用这个特性来获得性能的最大提升

    ChannelHandlerContext 和 ChannelHandler 之间的关联(绑定)是永远不会变的,所以缓存它的引用是安全的

6.3.1 使用 ChannelHandlerContext

    在 Netty 程序中,我们通过入站 Handler 处理完数据之后,一般会在最后收到事件流的入站 Handler 处理完逻辑后,往远端写一个响应消息。如下图


    

    从图中可以看到事件A 最后来到 4 号入站 Hander,处理完之后,在 4 号 Handler 中写入以响应事件 B,写入的方法有三种

    1. 通过 Channel 写入:channel.write(事件 B )

    事件流向为:4 号 ---> 5号 --->3 号 --->远端

    2. 通过 ChannelPipeLine 写入:pipeline.write(事件 B)

    事件流向为:4 号 ---> 5号 --->3 号 --->远端

    3. 通过 ChannelHandlerContext 写入:channelHandlerContext.write(事件 B )

    事件流向为:4 号 --->3 号 --->远端

    ChannelHandler 之间的事件流传递都是通过 ChannelHandlerContext 来实现的,从代码层面来看,其实就是调用了 ChannelHandlerContext 的 fireXXX()方法来触发下一个 ChannelHandler 的 XXX()方法。

6.3.2 ChannelHandler 和 ChannelHandlerContext 的高级用法

    1. 使用 @Sharable 标记共享 ChannelHandler

    在多个 ChannelPipeLine 中安装同一个 ChannelHandler时,需要在该 ChannelHandler 上用 @Sharable 注解标记。在使用共享 ChannelHandler 的时候尤其要注意是否线程安全。其用法如下:

@Sharable
public class SharableHandler extends ChannelHandlerAdapter{
    // do something
}

    共享 ChannelHandler 的目的在于统计一些共有的信息(比如系统访问次数)。

6.4 异常处理

6.4.1 入站异常

    对于出站异常,我们只需要继承 exceptionCaught 方法,在里面完成相应的异常处理逻辑即可

6.4.2 出站异常

    对于异常,通常的做法是:对调用给定的方法返回的 ChannelFuture 添加一个监听,待操作完成后,得到通知。典型做法如下

ChannelFuture future = channel.write(msg);
future.addListener(new ChannelFutureListener()){
  @Override
  public void operationComplete(ChannelFuture f){
      if(!f.isSuccess()){
          f.cause.printStackTrace();
          f.channel().close;
      }
  }
}

总结

    本章主要学习了事件是如何在 ChannelHandler 之间进行传递的,以及对于一些异常的处理。下一章节讲学习 Netty 的 EventLoop 和 并发模型

 
 




    

    

        

猜你喜欢

转载自blog.csdn.net/yhs1296997148/article/details/80463950