Channel的生命周期

生命周期状态:

状态 描述
ChannelUnregistered Channel已经被创建,但还未注册到EventLoop
ChannelRegistered Channel已经被注册到EventLoop
ChannelActive Channel处于活动状态(已经连接到它的远程节点)。它现在可以接收和发送数据了
ChannelInactive Channel没有连接到远程节点

状态发生改变的同时,将会生成对应的事件,这个事件将会被转发给ChannelPipeline中的ChannelHandler。
结合Channel的状态,回顾一下前面的内容:服务端的(ServerBootstrap)需要两个EventLoopGroup,第一组只包含一个ServerChannel用来处理“请求创建Channel”的EventLoop,第二组给Channel分配一个EventLoop。

ChannelHandler的生命周期

生命周期方法:

类型 描述
handlerAdded 当把ChannelHandler添加到ChannelPipeline中时被调用
handlerRemoved 当从ChannelPipeline中移除ChannelHandler时被调用
exceptionCaught 当处理过程中在ChannelPipeline中有错误产生时调用

在ChannelHandler被添加到ChannelPipeline中或者被从ChannelPipeline中移除时会调用这些操作,这些方法中的每一个都接收一个ChannelHandlerContext参数。
ChannelHandler两个重要的子接口:

  • ChannelInboundHandler 处理入站数据以及各种状态变化
  • CHannelOutboundHandler 处理出站数据并且允许拦截所有的操作

    ChannelInboundHandler接口

    ChannelInboundHandler的方法
类型 描述
channelRegistered 当Channel已经注册到它的EventLoop并且能够处理IO时被调用
channelUnregistered 当Channel从它的EventLoop注销并且无法处理任何IO时被调用
channelAvtive 当Channel处于活动状态时被调用,Channel已经连接/绑定并且已经就绪
channelInactive 当Channel离开活动状态并且不再连接它的远程节点时被调用
channelReadCompute 当Channel上的一个读操作完成时被调用(当所有可读字节都已经从Channel中读取之后,将会调用该回调方法,所以可能在channelReadCompute被调用之前看到多次调用channelRead)
channelRead 当从Channel读取数据时被调用
channelWritabilityChanged 当Channel的可写状态发生改变时调用。用户可以确保写操作不会完成得太快(避免OutOfMemoryError)或者可以在Channel变为再次可写时恢复写入。可以通过调用Channel的isWritable()来检测Channel的可写性,与可写相关的阈值可以通过Channel.config().setWriteHighWaterMark()和Channel.config().setWriteLowWaterMark()方法设置
userEventTriggered 当ChannelInboundHandler.fireUserEventTriggered()方法被调用时被调用,因为一个POJO被传经了ChannelPipeline

灰色的两个方法这两个在目前的学习阶段还不是很理解,后面再补充。
当某个ChannelInboundHandler的实现重写channelRead()方法时,它将负责显式地释放-与池化的ByteBuf相关的内存,Netty为此提供了实用的方法ReferenceCountUtil.release()。

  1. @Sharable
  2. public class DiscardHandler extends ChannelInboundHandlerAdapter { // 扩展的适配器类
  3. @Override
  4. public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {
  5. // 丢弃已接收的消息
  6. ReferenceCountUtil.release(msg);
  7. }
  8. }

Netty使用WARN级别的日志消息记录未释放的资源,可以非常简单地在代码中发现不规范的实例,以这种方式管理资源比较繁琐,Netty提供了一个更简单的方式是使用SimpleChannelInboundHandler类,它将会自动释放资源,在这个实例中不应该存储指向任何消息的引用供将来使用,因为这些引用都将失效。

  1. @Sharable
  2. public class SimpleDiscardHandler extends SimpleChannelInboundHandler<Object> { // 扩展的适配器类
  3. @Override
  4. protected void channelRead0(ChannelHandlerContext ctx, Object msg) throws Exception {
  5. // 自动释放
  6. }
  7. }

ChannelOutboundHandler接口

出站操作和数据将由ChannelOutboundHandler处理,它的方法将被Channel、ChannelPipeline以及ChannelHandlerContext调用。
ChannelOutboundHandler的一个强大的功能时可以按需推出操作或者事件,这使得可以通过一些复杂的方法来处理请求,例如远程节点的写入被暂停了,那么可以推迟冲刷操作并在稍后继续。
ChannelOutboundHandler的方法:

类型 描述
bind(ChannelHandlerContext,SocketAddrss,ChannelPromise) 当请求将Channel绑定到本地地址时被调用
connect(ChannelHandlerContext,SocketAddrss,SocketAddrss,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将数据写到远程节点时被调用

ChannelPromise和ChannelFuture

ChannelOutboundHandler中的大部分方法都需要一个ChannelPromise参数,以便在操作完成时得到通知。ChannelPromise是ChannelFuture的一个子类,其定义了一个可写的方法,如setSuccess()何setFailure(),从而使ChannelFuture不可变(当一个Promise完成之后,其对应的Future的值便不能在进行修改了)。

ChannelHandler的适配器

可以使用ChannelInboundHandlerAdapter和ChannelOutboundHandlerAdapter类作为自己的ChannelHandler的起始点,这两个适配器分别提供了ChannelInboundHandler和ChannelOutboundHandler的基本实现,通过扩展抽象类ChannelHandlerAdapter,它们获得了它们共同的超接口ChannelHandler的方法。
ChannelHandler家族 - 图1
ChannelHandlerAdapter提供了使用的方法isSharable(),如果其对应的实例被标注为Sharable,那么这个方法将返回true,表示它可以被添加到多个ChannelPipeline中。
在ChannelInboundHandlerAdapter和ChannelOutboundHandlerAdapter中所提供的方法体调用了其相关联的ChannelHandlerContext上的等效方法,从而将事件转发到了ChannelPipeline中的下一个ChannelHandler中。

补充:
2022年7月5日22:59:26,ChannelHandler默认是线程安全的,默认只允许一个Pileline实例拥有该对象,新建的Pipeline会抛出异常

  1. io.netty.channel.ChannelPipelineException: cn.linguo.netty.handler.EchoServerHandler is not a @Sharable handler, so can't be added or removed multiple times.

如果要让ChannelHandler在Pipeline之间共享,需要加上@Sharable注解,但必须保证该ChannelHandler是线程安全的,比如不要出现“int count=0”这样的共享变量。

资源管理—后续再扩展