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()。
@Sharablepublic class DiscardHandler extends ChannelInboundHandlerAdapter { // 扩展的适配器类@Overridepublic void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {// 丢弃已接收的消息ReferenceCountUtil.release(msg);}}
Netty使用WARN级别的日志消息记录未释放的资源,可以非常简单地在代码中发现不规范的实例,以这种方式管理资源比较繁琐,Netty提供了一个更简单的方式是使用SimpleChannelInboundHandler类,它将会自动释放资源,在这个实例中不应该存储指向任何消息的引用供将来使用,因为这些引用都将失效。
@Sharablepublic class SimpleDiscardHandler extends SimpleChannelInboundHandler<Object> { // 扩展的适配器类@Overrideprotected void channelRead0(ChannelHandlerContext ctx, Object msg) throws Exception {// 自动释放}}
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的方法。
ChannelHandlerAdapter提供了使用的方法isSharable(),如果其对应的实例被标注为Sharable,那么这个方法将返回true,表示它可以被添加到多个ChannelPipeline中。
在ChannelInboundHandlerAdapter和ChannelOutboundHandlerAdapter中所提供的方法体调用了其相关联的ChannelHandlerContext上的等效方法,从而将事件转发到了ChannelPipeline中的下一个ChannelHandler中。
补充:
2022年7月5日22:59:26,ChannelHandler默认是线程安全的,默认只允许一个Pileline实例拥有该对象,新建的Pipeline会抛出异常
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”这样的共享变量。
