一、ChannelHandler接口
ChannelHandler 为 Netty 中最核心的组件,它充当了所有处理入站和出站数据的应用程序逻辑的容器。ChannelHandler 主要用来处理各种事件,这里的事件很广泛,比如可以是连接、数据接收、异常、数据转换等。
ChannelHandler 有两个核心子类 ChannelInboundHandler 和 ChannelOutboundHandler,其中 ChannelInboundHandler 用于接收、处理入站数据和事件,而 ChannelOutboundHandler 则相反。Handler用适配器模式的Adapter对Handler接口进行了空实现,类图关系如下:
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接口处理入站事件和入站数据,提供的事件方法如下图:
- 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参数,以便在操作完成时得到通知。
- 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提供了抽象基类ChannelInboundHandlerAdapter 和ChannelOutboundHandlerAdapter。
ChannelHandlerAdapter 还提供了实用方法isSharable()。如果其对应的实现被标注为Sharable,那么这个方法将返回true,表示它可以被添加到多个ChannelPipeline。
二、ChannelPipeline 接口
每一个新创建的Channel 都将会被分配一个新的ChannelPipeline。这项关联是永久性的;Channel 既不能附加另外一个ChannelPipeline,也不能分离其当前的。
- ChannelPipeline本质上是ChannelHandler链的容器
- ChannelHandler是处理Channel上的入站和出站事件的代码。
- ChannelHandler对象接收事件触发并执行实现的业务逻辑,接着传递给链中的下一个ChannelHandler处理
- 一个入站事件被读取,从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