netty5.0之 ChannelPipeline和ChannelHandler

来源:互联网 发布:excel 数据保护 编辑:程序博客网 时间:2024/05/16 04:31

来自《Netty权威指南》 李林锋 著。

Netty的ChannelPipeline和ChannelHandler机制类似于Servlet和Filter过滤器,这类拦截器实际上上职责链模式的一种变形,主要是为了方便时间的拦截和用户业务逻辑的定制。Netty的Channel过滤器将Channel的数据管道抽象为ChannelPipeline,消息在ChannelPipeline中流动和传递。ChannelPipe持有I/O事件拦截器ChannelHandler的链表,由ChannelHandler对I/O时间进行拦截和处理,可以方便地通过新增和删除ChannelHandler来实现不同的业务逻辑定制,不需要对已有的ChannelHandler进行修改,能够实现对修改封闭和对扩展开放的支持。

下面我们队ChannelPipeline和ChannelHandler,以及与之相关的ChannelHandlerContext进行详细介绍和源码分析,内容包括

 * ChannelPipeline功能说明

 * ChannelPipeline源码分析 

 * ChannelHandler功能说明

 * ChannelHandler源码分析


ChannelPipeline功能说明

ChannelPipeline是ChannelHandler的容器,它负责ChannelHandler的管理和事件拦截与调度

1、流程

一个消息被p的ChannelHandler链拦截和处理的全过程,消息的读取和发送处理全流程描述如下:

(1)底层的SocketChannel read()方法读取byteBuf,触发ChannelRead事件,由I/O线程NioEventLoop 调用  ChannelPipeline 的 fireChannelRead(Object msg)方法,将消息(ByteBuf) 传输到ChannelPipeline中;

(2)消息依次被HeadHandler、ChannelHandler1、ChannelHandler2 ....TailHandler拦截和处理,在这个过程中,任何ChannelHandler都可以中断当前的流程,结束消息的传递;

(3)调用ChannelHandlerContext的write方法发送消息,消息从TailHandler开始,途径ChannelHandlerN .....ChannelHandler1、HeadHandler,最终被添加到消息发送缓存区中等待刷新和发送,在此过程中也可以中断消息的传递,例如当编码失败时,就需要中断流程,构造异常的Future返回。


Netty中的事件分为inbound事件和outbound事件。inbound事件通常由I/O线程触发(按照我的理解是框架自己已经实现好的,被动接受的数据流传输)。例如TCP链路建立事件、链路关闭事件、读事件、异常通知事件等,它对应上图中的左半部分。

2、触发

触发inbound事件的方法如下:红色四方块标注的;单线标注的为outbound事件



3、自定义拦截器

ChannelPipeline通过 ChannelHandler 接口来实现事件的拦截和处理,由于ChannelHandler中的时间种类繁多,不同的ChannelHandler可能只需要关心其中的某一个或者几个事件,所以,通常ChannelHandler只需要继承ChannelHandlerAdapter类覆盖自己关心的方法即可。

4、pipeline的构建

事实上,用户不需要自己创建pipeline,因为使用ServerBootStrap或者BootStrap启动服务端或者客户端时,Netty会为每个Channel连接创建一个独立的pipeline。对于使用者而言,只需要将自定义的拦截器加入到pipeline即可。相关的代码如下:

ch.pipeline().addLast("http-decoder",new HttpRequestDecoder());//解压器,将多个消息转换为单一的FullHttpRequest或FullHttpResponse,原因是http解压器在每个HTTP消息中会生成多个消息对象
ch.pipeline().addLast("http-aggregator",new HttpObjectAggregator(65536));
ch.pipeline().addLast("http-encoder", new HttpResponseEncoder());
//支持异步发送大的码流(例如大的文件传输),但不占用过多的内存,防止发生java内存溢出错误。
ch.pipeline().addLast("http-chunked", new ChunkedWriteHandler());
ch.pipeline().addLast("fileServerHandler", new HttpFileServerHandler(url));

对于类似编解码这样的ChannelHandler,它存在先后顺序,例如MessageToMessageDecoder,在它之前往往需要有ByteToMessageDecoder将ByteBuf解码为对象,然后对对象做二次解码得到最终的POJO对象。Pipeline支持指定位置添加或者删除拦截器,相关接口定义如下:


5、ChannelPipeline的主要特性

ChannelPipeline支持运行多态的添加或者删除ChannelHandler,在某些场景下这个特性非常有用。例如当业务高峰期需要对系统做拥塞保护时,就可以根据当前的系统时间进行判断,如果处于业务高峰期,则动态地将系统拥塞保护ChannelHandler添加到当前的ChannelPipeline中,当高峰期过后,就可以动态删除拥塞保护ChannelHandler了。

ChannelPipeline是线程安全的,这意味着N个业务线程可以并发地操作ChannelPipeline而不承诺在多线程并发问题。但是,ChannelHandler却不是线程安全的,这意味着尽管ChannelPipeline是线程安全的,但是用户仍然需要自己保证ChannelHandler的线程安全。

ChannelPipeline源码分析

1、 ChannelPipeline的代码相对比较简单,它实际上是一个ChannelHandler的容器,内部维护了一个ChannelHandler的链表和迭代器,可以方便实现ChannelHandler查找、添加、替换和删除。它的继承关系比较简单如下:

  public interface ChannelPipeline extends Iterable<Entry<String, ChannelHandler>> 

          final class DefaultChannelPipeline implements ChannelPipeline 

2、ChannelPipeline对ChannelHandler的管理,拿DefaultChannelHandler说明

1)

static final InternalLogger logger = InternalLoggerFactory.getInstance(DefaultChannelPipeline.class);    @SuppressWarnings("unchecked")    private static final WeakHashMap<Class<?>, String>[] nameCaches =            new WeakHashMap[Runtime.getRuntime().availableProcessors()];    static {        for (int i = 0; i < nameCaches.length; i ++) {            nameCaches[i] = new WeakHashMap<Class<?>, String>();        }    }    final AbstractChannel channel;    final DefaultChannelHandlerContext head;    final DefaultChannelHandlerContext tail;    private final Map<String, DefaultChannelHandlerContext> name2ctx =        new HashMap<String, DefaultChannelHandlerContext>(4);    final Map<EventExecutorGroup, ChannelHandlerInvoker> childInvokers =            new IdentityHashMap<EventExecutorGroup, ChannelHandlerInvoker>();
2)

未完,待续

0 0