Netty-10-ChannelHandler API
学习方式
官方文档
《Netty in Action》
blogs
源码
Channel 生命周期
channel 有以下四种生命状态。
状态 描述
ChannelUnregistered Channel已经被创建,但未注册到EventLoop
ChannelRegistered Channel已经被注册到了EventLoop
ChannelActive Channel处于活动状态(已经连接到它的远程节点)。现在Channel可以接受和发送数据
ChannelInActive Channel没有连接到远程节点一般Channel的生命周期顺序 ChannelRegistered -> ChannelActive -> ChannelInactive -> ChannelUnregistered。
当Channel的状态发生变化时,将会生成对应的事件。
与此同时,这些事件会被转发给ChannelPipeline中的ChannelHandler。
ChannelHandler 生命周期
ChannelHandler定义的生命周期操作,在ChannelHandler被添加到ChannelPipeline中或者被从ChannelPipeline中移除时会调用这些方法。
这些方法中都可以接受一个ChannelHandlerContext参数。
类型 描述
handlerAdded 当把ChannelHandler添加到ChannelPipeline中时被调用
handlerRemoved 当从ChannelHandler在ChannelPipeline移除时调用
exceptionCaught 当处理过程中在ChannelPipeline中有错误产生时被调用Netty中定义了下面两个重要的ChannelHandler接口:
ChannelInboundHandler——处理入站数据以及各种状态变化
CHannelOutboundHandler——处理出站数据并且允许拦截所有的操作
ChanneInboundHandler接口
类型 描述
channelRegistered 当Channel已经注册到它的EventLoop并且能够处理I/O时被调用
channelUnregistered 当Channel从它的EventLoop注销并且无法处理任何I/O时被调用
channelActive 当Channel处于活动状态时被调用;Channel已经连接/绑定并且已经就绪
channelInactive 当Channel离开活动状态并且不再连接它的远程节点时被调用
channelReadComplete 当Channel的一个读操作完成时被调用
channelRead 当从Channel读取数据时被调用
channelWritabilityChanged 当Channel的可写状态发生改变时被调用。用户可以确保写操作不会完成的太快(以避免发生OutOfMemoryError)或者可以在Channel变为再次可写时恢复写入。Channel的isWriteable()方法可以来检测Channel的可写性。与可写性相关的阀值可以通过Channel.config().setWriteHighWaterMark()和Channel.config().setWriteLowWaterMark()方法来设置
userEventTriggered 当ChannelInboundHandler.fireUserEventTriggered()方法被调用时被调用。- 释放消息资源
当某个ChannelInboundHandler 的实现重写channelRead()方法时,它将负责显式地释放与池化的ByteBuf 实例相关的内存。
Netty 为此提供了一个实用方法 ReferenceCountUtil.release(),如代码清单6-1 所示。
@Sharable
public class DiscardHandler extends ChannelInboundHandlerAdapter {
@Override
public void channelRead(ChannelHandlerContext ctx, Object msg) {
ReferenceCountUtil.release(msg);
}
}Netty 将使用WARN 级别的日志消息记录未释放的资源,使得可以非常简单地在代码中发现违规的实例。
但是以这种方式管理资源可能很繁琐。一个更加简单的方式是使用 SimpleChannelInboundHandler。
代码清单6-2 是代码清单6-1 的一个变体,说明了这一点。
- SimpleChannelInboundHandler 方式
public class SimpleDiscardHandler extends SimpleChannelInboundHandler {
@Override
public void channelRead0(ChannelHandlerContext ctx,Object msg) {
// No need to do anything special
}
}由于SimpleChannelInboundHandler 会自动释放资源,所以你不应该存储指向任何消息的引用供将来使用,因为这些引用都将会失效。
ps: netty 在设计的时候就尽可能的考虑使用者的便利性,这是一个优秀的框架所必备的设计思想。
ChannelOutboundHandler 接口
出站操作和数据将由ChannelOutboundHandler处理。
它的方法将被Channel、ChannelPipeline以及ChannelHandlerContext调用。
ChannelOutboundHandler 可以按需推迟操作或者事件。
类型 描述
bind(ChannelHandlerContext, SockertAddress, ChannelPromise) 当请求将Channel绑定到本地地址时被调用
connect(ChannelHandlerContext, SocketAddress, SockertAddress, 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
ChannelPromise与ChannelFuture ChannelOutboundHandler中的大部分方法都需要一个ChannelPromise参数,以便在操作完成时得到通知。
ChannelPromise是ChannelFuture的一个子类,其定义了一些可写的方法,如setSuccess()和setFailure(),从而使ChannelFuture不可变。
这里借鉴的是 Scala 的 Promise 和 Future 的设计,当一个 Promise 被完成之后,其对应的 Future 的值便不能再进行任何修改了
ps: 思想是相同的。比如 node.js 的回调,js 的 promise 回调模式。
ChannelInboundHandler和ChannelOutboundHandler的区别
个人感觉in和out的区别主要在于ChannelInboundHandler的channelRead和channelReadComplete回调和ChannelOutboundHandler的write和flush回调上,
ChannelOutboundHandler的channelRead回调负责执行入栈数据的decode逻辑,ChannelOutboundHandler的write负责执行出站数据的encode工作。
其他回调方法和具体触发逻辑有关,和in与out无关。
ChannelHandler 适配器
ChannelInboundHandlerAdapter和ChannelOutboundHandlerAdapter两个适配器分别提供了ChannelInboundHandler和ChannelOutboundHandler的基本实现。
通过扩展抽象类ChannelHandlerAdapter,它们获得了它们共同的超接口ChannelHandler的方法。
ChannelHandlerAdapter提供了isSharable(),如果其对应的实现被注解标注为Sharable,这方法将返回true,表示它可以被添加到多个ChannelPipeline。
ChannelInboundHandlerAdapter和ChannelOutboundHandlerAdapter中的方法体调用了其相关联的ChannelHandlerContext上的等效方法,从而将事件转发到了ChannelPipeline中的下一个ChannelHandler中。
个人总结
《netty in action》是一个系统的讲解 netty 的书籍,但是说实话不适合入门学习。
比较适合在会使用 netty 之后,然后回过头来阅读,加深对 netty 的理解。
因为这种书籍不是从一个个示例开始的,而是从一大堆 api 和概念开始的。
参考资料
- channel handler
