我们了解了ByteBuf Netty的数据容器,我们将说下数据流和处理组件
我们可以在ChannelPipeline中将ChannelHandler连接在一起并进行处理,这就是本章的串联,以及一个特殊关系,ChannelHandlerContext
ChannelHandler是一个庞大的家族
在学习不同的ChannelHandler的特殊性之前,我们先说下共性
1.Channel的生命周期
Interface Channel有一组和ChannelInboundHandler相关的状态模型,分别是
ChannelUnregistered 已经被创建,还没注册EventLoop
ChannelRegistered 已经注册到了EventLoop
ChannelActive 处于活动状态(连接到远程节点),可以发送接收数据
ChannelInactive 没有连接到远程节点
Channel的正常生命周期如下,状态发生改变的时候,会生成对应的事件
ChannelHandler的生命周期
而一个Handler被加入ChannelPipeline或者从ChannelPipline移除的时候,会调用如下的操作
Netty定义了两个重要的Handler的子接口
ChannelInboundHandler — 处理入站数据以及各种数据变化
ChannelOutboundHandler — 处理出站数据并拦截所有的操作
那么,我们仔细的说道说道两个子接口
1.ChannelInboundHandler,可以在数据被接收时候或者Channel状态改变的时候调用,都与Channel的生命周期相关
如果某个ChannelInboundHandler的实现重写 channelRead()方法的时候,需要负责显式的释放ByteBuf相关的实例,Netty为此提供了一个实用的方法 ReferenceCountUtil.release()
由这个Handler去释放资源
如果不希望手动的去调用release方法,则可以继承SimpleChannelInboundHandler,这个类会在读取后自动释放
2.ChannelOutboundHandler接口,
管理所有出站的操作和数据,会被Channel ChannelPipeline 和 ChannelHandlerContext调用
3.ChannelHandlerAdapter
常见的使用方式就是继承ChannelInboundHandlerAdapter和ChannelOutboundHandlerAdapter作为自己的ChannelHandler的起点
其类图如下
方便我们简单的扩展来使用对应的方法
4.资源管理
每当调用ChannelInboundHandler.channelRead()或者 ChannelOutboundHandler.write处理数据的时候,都需要保证没有资源泄露,Netty使用引用计数处理池化资源,所以需要使用后调整基数
Netty还提供了class ResouceLeakDetector 来对应用程序的采样并检测
还有对应的检测级别
DISABLED 禁用检测
SIMPLE 使用1%的采样率检测
ADVANCED 默认采样率
PARANOID 每次都对消息进行采样
定义如下
java -Dio.netty.leakDetectionLevel=ADVANCED
对于代码来说,如果希望消息被自己消费并且不会传递给下一个ChannelInboundHandler
对于read可以如下的进行调用
确认释放之后,就可以保证不会将消息传递给下一个ChannelInboundHandler
丢弃出站消息,可以如下
进行丢弃,说明没有必要传递给下一个Handler了,还需要通知ChannelPromise,告知Listener已经被处理的情况
ChannelPipeline接口
一个拦截流经Channel的入站和出站时间的ChannelHandler实例链
整体的流程如下,我们通过ChannelPipeline传播事件,从头部开始一直传播到尾端
在ChannelPipeline传播事件时候,测试ChannelPipeline中的下一个ChannelHandler是否是当前方向上的Handler,不然就跳过,当然,有的ChannelHandler可以同时实现ChannelInboundHandler和ChannelOutboundHandler
修改ChannelPipeline
ChannelHandler可以通过添加 修改 替换其他的ChannelHandler来实时地修改ChannelPipeline的布局,这是ChannelHandler的动态布局,ChannelHanler和ChannelPipeline交互的方法
常见的可以修改ChannelHandler的Pipeline的api有
一个简单的例子就是
除了主要交互的API,还有些特殊的操作api
ChannelPipeline的api有着出站和入站的附加方法,相关的事件有
入站操作
出站相关操作
ChannelPipeline 保存了与Channel相关的ChannelHandler
ChannelPipeline 可以根据需要 添加或者删除ChannelHandler动态修改
ChannelPipeline 提供了丰富的API供调用,方便响应出站和入站操作
如何使用ChannelHandlerContext
首先是ChannelHandlerContext ChannelHandler Channel ChannelPipeline的关系图
整体的流程图如下,我们可以通过ChannelHandlerContext获取到Channel的引用,调用对应的write方法,让事件流经整个ChannelPipeline
我们写入ChannelPipeline,到ChanelPipline的引用也是用过ChannelHandlerContext获取的
整体的事件流动是通过ChannelHandlerContext进行通信的
还有一些高级的用法
我们可以通过调用ChannelHandlerContext上的pipeline()方法来获得ChannelPipeline,从而操控其他ChannelHandler
这样,我们可以使用操控来实现动态的协议操作
另一种就是在其缓存ChannelHandlerContext引用,供日后使用
一个ChannelHandler可以从属多个ChannelPipeline,所以在其中保存ChannelHandlerContext可能导致异常,即使加上了@Sharable,因为保存了状态,为了支持共享,建议ChannelHandler做成线程安全的
像下面的共享Handler,因为内部有状态,所以是可能出现问题
将这个类的一个实例添加到ChannelPipeline可能导致并发请求出现问题,可以保证count是线程安全的来修正
至于为何共享ChannelHandler,可以在多个ChannelPipeline中安装一个ChannelHandler来收集不同Channel的信息