Netty关键组件ChannelHandler、ChannelPipeline、Ctx

一、ChannelHandler接口

ChannelHandler 为 Netty 中最核心的组件,它充当了所有处理入站和出站数据的应用程序逻辑的容器。ChannelHandler 主要用来处理各种事件,这里的事件很广泛,比如可以是连接、数据接收、异常、数据转换等。

ChannelHandler 有两个核心子类 ChannelInboundHandler 和 ChannelOutboundHandler,其中 ChannelInboundHandler 用于接收、处理入站数据和事件,而 ChannelOutboundHandler 则相反。Handler用适配器模式的Adapter对Handler接口进行了空实现,类图关系如下:

ChannelHandler类图

1.Channel的生命周期

  • ChannelUnregistered ----- Channel已经被创建,但未注册到EventLoop上
  • ChannelRegistered ----- Channel已经被注册到EventLoop上
  • ChannelActive ----- Channel处于活动状态。对于Tcp客户端是只有与远程建立连接后,channel才会变成Active。udp是无连接的协议,Channel一旦被打开,便激活。注意这点不同点
  • ChannelInactive ----- Channel处于关闭状态。常用来发起重连或切换链路

1.ChannelHandler的生命周期

ChannelHandler被添加到ChannelPipeline中或者被从ChannelPipeline中移除时将调用下列操作:

2.ChannelInboundHandler 接口<重点>

ChannelInboundHandler接口处理入站事件和入站数据,提供的事件方法如下图:

image
  • channelRegistered,当Channel 已经注册到它的EventLoop 并且能够处理I/O 时被调用
  • channelUnregistered,当Channel 从它的EventLoop 注销并且无法处理任何I/O 时被调用
  • channelActive,当Channel 处于活动状态时被调用;Channel 已经连接/绑定并且已经就绪
  • channelInactive,当Channel 离开活动状态并且不再连接它的远程节点时被调用
  • channelReadComplete,当Channel上的一个读操作完成时被调用①
  • channelRead,当从Channel 读取数据时被调用
  • ChannelWritability-Changed,当Channel 的可写状态发生改变时被调用。用户可以确保写操作不会完成得太快(以避免发生OutOfMemoryError)或者可以在Channel 变为再次可写时恢复写入。可以通过调用Channel 的isWritable()方法来检测Channel 的可写性。与可写性相关的阈值可以通过Channel.config().setWriteHighWaterMark()和Channel.config().setWriteLowWater-Mark()方法来设置。
  • userEventTriggered,当ChannelnboundHandler.fireUserEventTriggered()方法被调用时被调用。

当某个ChannelInboundHandler 的实现重写channelRead()方法时,它要负责显式地释放与池化的ByteBuf 实例相关的内存,Netty 为此提供了一个实用方法ReferenceCount-Util.release()。当然还有一个更加简单的方式是使用Simple-ChannelInboundHandler,SimpleChannelInboundHandler会自动释放资源。

@Sharable
public class DiscardHandler extends ChannelInboundHandlerAdapter {
    @Override
    public void channelRead(ChannelHandlerContext ctx, Object msg) {
    ReferenceCountUtil.release(msg);
}

3.ChannelOutboundHandler 接口

出站数据和事件将由ChannelOutboundHandler处理。ChannelOutboundHandler大部分方法都需要一个ChannelPromise参数,以便在操作完成时得到通知。

image
  • bind(ChannelHandlerContext,SocketAddress,ChannelPromise)
    当请求将Channel 绑定到本地地址时被调用
  • connect(ChannelHandlerContext,SocketAddress,SocketAddress,ChannelPromise)
    当请求将Channel 连接到远程节点时被调用
  • disconnect(ChannelHandlerContext,ChannelPromise)
    当请求将Channel 从远程节点断开时被调用
  • close(ChannelHandlerContext,ChannelPromise) 当请求关闭Channel 时被调用
  • deregister(ChannelHandlerContext,ChannelPromise)
    当请求将Channel 从它的EventLoop 注销时被调用
  • read(ChannelHandlerContext) 当请求从Channel 读取更多的数据时被调用
  • flush(ChannelHandlerContext) 当请求通过Channel 将入队数据冲刷到远程节点时被调用
  • write(ChannelHandlerContext,Object,ChannelPromise) 当请求通过Channel 将数据写到远程节点时被调用

4.ChannelHandler的Adapter适配器

有一些适配器类可以将编写自定义的ChannelHandler所需要的努力降到最低限度,因为它们提供了定义在对应接口中的所有方法的默认实现,因为有时会忽略那些不感兴趣的事件,所以Netty提供了抽象基类ChannelInboundHandlerAdapterChannelOutboundHandlerAdapter

image

ChannelHandlerAdapter 还提供了实用方法isSharable()。如果其对应的实现被标注为Sharable,那么这个方法将返回true,表示它可以被添加到多个ChannelPipeline。

二、ChannelPipeline 接口

每一个新创建的Channel 都将会被分配一个新的ChannelPipeline。这项关联是永久性的;Channel 既不能附加另外一个ChannelPipeline,也不能分离其当前的。

  1. ChannelPipeline本质上是ChannelHandler链的容器
  2. ChannelHandler是处理Channel上的入站和出站事件的代码。
  3. ChannelHandler对象接收事件触发并执行实现的业务逻辑,接着传递给链中的下一个ChannelHandler处理
image
  • 一个入站事件被读取,从ChannelPipeline头部开始流动,传递给第一个ChannelInBoundHandler
  • 一个出站事件触发,从链路尾端的ChannelOutboundHandler开始流动,直到它到达链的头部为止。
  • 不要阻塞ChannelChandler,否则可能会影响其他的Channel处理。
ChannelPipeline pipeline = null ;
ServerHandller firstHandller = new ServerHandller();
pipeline.addLast("handler1", firstHandller);
pipeline.addLast("handler2", new ServerHandller());
pipeline.addLast("handler3", new ServerHandller());
pipeline.remove("handler2");
pipeline.remove(firstHandller);
pipeline.replace("handler3", "newHandler", firstHandller);
//返回和 ChannelHandler 绑定的 ChannelHandlerContext
pipeline.context("newHandler");
pipeline.get("newHandler");

ChannelHandler可以使用@Sharable注解标注,可以将一个ChannelHandler绑定到多个ChannelPipeline链中,也就绑定到多个ChannelhandlerContext,但需注意线程安全!!!

总结:ChannelPipeline保存了与Channel相关联的ChannelHandler,ChannelPipeline可以根据需要,通过添加或者删除ChannelHandler来动态地修改

三、ChannelHandlerContext接口

ChannelHandlerContext代表了ChannelHandler和ChannelPipeline之间的关联,每当ChannelHandler添加到ChannelPipeline中时,都会创建ChannelHandlerContext。

1.压缩事件源,如果这个对象确认不需要经过其他 outBound 类型的 handler 处理,就使用这个方法。

ctx.writeAndFlush() 

2.传递事件源,如果需要把ctx对象往前进行传播,当前创建的对象需要经过后面的 outBound 类型的 handler,那么就调用此方法。

ctx.channel().writeAndFlush()

参考引用:


Netty基础:细说ChannelHandler和ChannelPipeLine
Netty专栏 ——— ChannelHandler和ChannelPipelin

推荐阅读更多精彩内容