我们了解了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的信息

发表评论

邮箱地址不会被公开。 必填项已用*标注