netty实战笔记 第六章 ChannelHandler和ChannelPipeline
6.1 ChannelHandler家族
6.1.1 Channel的生命周期
Channel有四种状态.如下;
状态 | 描述 |
---|---|
channelUnregistered | channel已经被创建,但还未注册到EventLoop |
ChannelRegistered | Channel已经被注册到了EventLoop |
ChannelActive | Channel处于活动状态(已经连接到它的远程节点),它现在可以接受和发送数据 |
ChannelInactive | Channel没有连接到远程节点 |
6.1.2 ChannelHandler的生命周期
ChannelHandler有三个状态。在ChannelHandler被添加到ChannelPipeline中或者被移除时调用这些操作。
| 类型 | 描述 |
| handlerAdded | 当把ChannelHandler添加到ChannelPipeline中时被调用|
| handlerRemoved | 当从ChannelPipeline中移除ChannelHandler的时候调用|
| exceptionCaught | 当处理过程中在ChannelPipeline中有错误产生的时候调用 |
两个子类:ChanneInboundHandler,ChannelOutboundHandler
6.1.3 ChannelInboundHandler 接口
处理入站数据以及各种状态变化
将在数据被接受的时候或者与其对应的Channel状态发生改变的时候调用。如下:
类型(对应着方法名) | 描述 |
---|---|
channelRegistered | 当channel已经注册到它的EventLoop并且能处理IO时调用 |
channelUnregistered | 当channel从它的EventLoop注销并且无法处理任何IO时被调用 |
channelActive | 当channel处理活动状态时被调用,channel已经连接/绑定并且已经就绪 |
channeInactive | 当channel离开活动状态并且不再连接他的远程节点时调用 |
ChannelReadComplete | 当channel上的一个读操作完成时被调用 |
channelRead | 当从channel中读取数据时候被调用 |
channelWritabilityChanged | 当channel的可写状态发生改变时被调用。用户可以确保写操作不会完成的太快,或者可以在Channel变为再次可写时恢复写入.可以通过Channel的isWritable()方法来检测channel的可写性。与可写性相关的阈值可以通过channel.config().setWriteHighWaterMark()和channel.config().setWriteLowWaterMark()方法来设置。 |
userEventTriggered | 当channelInboundHandler.fireUseEventtriggered()方法被调用时被调用,应为一个POJO被传经了ChannelPipeline。 |
当某个ChannelInboundHandler的实现重写了channelRead()方法时,它将显示的释放与池化的Bytebuf相关的内存。
6.1.4 ChannelOutboundHandler接口
处理出站操作和数据。 其方法将被Channel,channelPipeline,已经ChannelHandlerContext调用。
其可以按需推迟操作或者事件,可以通过一些复杂的方法来处理请求。
类型 | 描述 |
---|---|
bind(ChannelHandlerContext,SocketAddress,ChannelPromis) | 当请求Channel绑定到本地地址时调用 |
connect(ChannelHandlerContext,SocketAddress,SocketAddress,ChannelPromise) | 当请求将Channel连接到远程节点的时候被调用 |
disconnect(ChannlelHandlerContext,ChannelPormise) | 当请求将Channel从远程节点断开的时候被调用 |
close(ChannelHandlerContext ,ChannelPromise) | |
当请求关闭Channel的时候被调用 | |
deregister(ChannelHandlerContext,ChannelPromise) | 当请求将从他的EventLoop注销时被调用 |
read(ChannelHandlerContext) | 当请求从Channel读取更多数据的时候被调用 |
flush(ChannelHandlerContext) | 当请求通过Channel将入队数据冲刷到远程节点的时候被调用 |
write(ChannelHandlerContext,Object,ChannelPromise) | 当请求通过Channel将数据写到远程节点的时候背调用 |
6.1.5 ChannelHandler适配器
ChannelHandlerAdapter提供给了isSharable(),如果对应的实现被注为Sharable,返回true
6.1.6 资源管理
Netty定义了4中泄露级别
级别 | 描述 |
---|---|
DISABLED | 禁止泄漏检测,只有在详尽的测试后才设置这个值 |
SIMPLE | 使用 1%的默认采样率检测并报告任何发现的泄露。这是默认级别,适合绝大部分的情况 |
ADVANCED | 使用默认的采样率,报告所发现的任何的泄露以及对应的消息被访问的位置 |
PARANOID | 类似于ADVANCED,但是其将会对每次(对消息的)访问都进行采样。这对性能将会有很 |
大的影响,应该只在调试阶段使用 |
6.2 ChannelPipeline接口
ChannelPipeline是一个连接流程Channel的入站和出站时间的ChannelHandler事件链。
每一个新创建的Channel都将被分配一个新的ChannelPipeline。Channel既不能附件另一个ChannelPipeline,也不能分离当前的。
ChannelHandlerContext使得ChannelHandler能够和它的ChannelPipeline以及其他的ChannelHandler 交互。ChannelHandler 可以通知其所属的ChannelPipeline 中的下一个ChannelHandler,甚至可以动态修改它所属的ChannelPipeline
6.2.1 修改ChannelPipeline
ChannelHandler可以通过添加,删除,或者替换其他的ChannleHandler来实时的修改ChannelPipeline的布局。
名称 | 描述 |
---|---|
addFirst,addBefore,addAfter,addLast | 将一个Channelhandler添加到ChannelPipeline |
remove | 讲一个CHannelhandler从ChannelPipeline中删除 |
replace | 将ChannelPipeline中的一个ChannelHandler替换为另一个ChannelHandler |
ChannelPipeline的访问CHannelHandler的方法。
名称 | 描述 |
---|---|
get | 通过类型或者名称返回ChannelHandler |
context | 返回和ChannelHandler绑定的ChannelHandlerContext |
names | 返回ChannellPipeline中所有ChannelHandler的名称 |
6.2.2 触发时间
用于通过ChannelInboundHandler在ChannelPipeline中发生的事件。
方法名称 | 描述 |
---|---|
fireChannelRegistered | 调用ChannelPipeline 中下一个ChannelInboundHandler 的channelRegistered(ChannelHandlerContext)方法 |
fireChannelUnregistered | 调用ChannelPipeline 中下一个ChannelInboundHandler 的channelUnregistered(ChannelHandlerContext)方法 |
fireChannelActive | 调用ChannelPipeline 中下一个ChannelInboundHandler 的channelActive(ChannelHandlerContext)方法 |
fireChannelInactive | 调用ChannelPipeline 中下一个ChannelInboundHandler 的channelInactiveChannelHandlerContext)方法 |
fireExceptionCaught | 调用ChannelPipeline 中下一个ChannelInboundHandler 的exceptionCaught(ChannelHandlerContext, Throwable)方法 |
fireUserEventTriggered | 调用ChannelPipeline 中下一个ChannelInboundHandler 的userEventTriggered(ChannelHandlerContext, Object)方法 |
fireChannelRead | 调用ChannelPipeline 中下一个ChannelInboundHandler 的channelRead(ChannelHandlerContext, Object msg)方法 |
fireChannelReadComplete | 调用ChannelPipeline 中下一个ChannelInboundHandler 的channelReadComplete(ChannelHandlerContext)方法 |
fireChannelWritability-Changed | 调用ChannelPipeline 中下一个ChannelInboundHandler 的channelWritabilityChanged(ChannelHandlerContext)方法 |
ChannelPipeline 的出站操作
方 法 名 称 | 描 述 |
---|---|
bind | 将Channel 绑定到一个本地地址,这将调用ChannelPipeline 中的下一个ChannelOutboundHandler 的bind(ChannelHandlerContext, Socket-Address, ChannelPromise)方法 |
connect | 将Channel 连接到一个远程地址,这将调用ChannelPipeline 中的下一个ChannelOutboundHandler 的connect(ChannelHandlerContext, SocketAddress, ChannelPromise)方法 |
disconnect | 将Channel 断开连接。这将调hannelPipeline 中的下一个ChannelOutbound-Handler 的disconnect(ChannelHandlerContext, Channel Promise)方法 |
close | 将Channel 关闭。这将调用ChannelPipeline 中的下一个ChannelOutbound-Handler 的closeChannelHandlerContext, ChannelPromise)方法 |
deregister | 将Channel 从它先前所分配的EventExecutor(即EventLoop)中注销。这将调用ChannelPipeline 中的下一个ChannelOutboundHandler 的deregister(ChannelHandlerContext, ChannelPromise)方法 |
flush | 冲刷Channel所有挂起的写入。这将调用ChannelPipeline 中的下一个Channel-OutboundHandler 的flush(ChannelHandlerContext)方法 |
write | 将消息写入Channel。这将调用ChannelPipeline 中的下一个Channel-OutboundHandler的write(ChannelHandlerContext, Object msg, Channel-Promise)方法。注意:这并不会将消息写入底层的Socket,而只会将它放入队列中。要将它写入Socket,需要调用flush()或者writeAndFlush()方法 |
writeAndFlush | 这是一个先调用write()方法再接着调用flush()方法的便利方法 |
read | 请求从Channel 中读取更多的数据。这将调用ChannelPipeline 中的下一个ChannelOutboundHandler 的read(ChannelHandlerContext)方法 |
6.3 ChannelHandlerContext接口
代表了ChannelHandler和ChannelPipeline之间的关联。
每当有ChannelHandler添加到ChannelPipeline中时,都会创建ChannelHandlerContext。
ChannelHandlerContext的主要功能是管理它所在的连接的ChannelHandler和在同一个ChannelPipeline中的其他的Channelhandler之间的交互。
下面的时候ChannelChandlerContext的API
方法名称 | 描述 |
---|---|
alloc | 返回和这个实例相关联的Channel 所配置的ByteBufAllocator |
bind | 绑定到给定的SocketAddress,并返回ChannelFuture |
channel | 返回绑定到这个实例的Channel |
close | 关闭Channel,并返回ChannelFuture |
connect | 连接给定的SocketAddress,并返回ChannelFuture |
deregister | 从之前分配的EventExecutor 注销,并返回ChannelFuture |
disconnect | 从远程节点断开,并返回ChannelFuture |
executor | 返回调度事件的EventExecutor |
fireChannelActive | 触发对下一个ChannelInboundHandler上的channelActive()方法(已连接)的调用 |
fireChannelInactive | 触发对下一个ChannelInboundHandler 上的channelInactive()方法(已关闭)的调用 |
fireChannelRead | 触发对下一个ChannelInboundHandler 上的channelRead()方法(已接收的消息)的调用 |
fireChannelReadComplete | 触发对下一个ChannelInboundHandler 上的channelReadComplete()方法的调用 |
fireChannelRegistered | 触发对下一个ChannelInboundHandler 上的fireChannelRegistered()方法的调用 |
fireChannelUnregistered | 触发对下一个ChannelInboundHandler上的fireChannelUnregistered()方法的调用 |
fireChannelWritabilityChanged | 触发对下一个ChannelInboundHandler 上的fireChannelWritabilityChanged()方法的调用 |
fireExceptionCaught | 触发对下一个ChannelInboundHandler 上的fireExceptionCaught(Throwable)方法的调用 |
fireUserEventTriggered | 触发对下一个ChannelInboundHandler 上的fireUserEventTriggered(Object evt)方法的调用 |
handler | 返回绑定到这个实例的ChannelHandler |
isRemoved | 如果所关联的ChannelHandler 已经被从ChannelPipeline中移除则返回true |
name | 返回这个实例的唯一名称 |
pipeline | 返回这个实例所关联的ChannelPipeline |
read | 将数据从Channel读取到第一个入站缓冲区;如果读取成功则触发①一个channelRead事件,并(在最后一个消息被读取完成后)通知ChannelInboundHandler 的channelReadComplete |
write | 通过这个实例写入消息并经过ChannelPipeline |
writeAndFlush | 通过这个实例写入并冲刷消息并经过ChannelPipeline |
注意
- channelHandlerContext和ChannelHandler之间的关联是永远不会改变的,所以缓存对它的引用是安全的。
- ChannelHandlerContext的方法将产生更短的事件流,应该尽可能的利用这个特性来获取最大的性能。
6.3.1 使用ChannelHandlerContext
先看下 ChannelHandlerContext,Channel,ChannelPipeline之间的关系。
下面的是事件从一个ChannelHandler到下一个ChannelHandler 的移ChannelHandlerContext 上的调用完成的。
[外链图片转存失败(img-vSQn27Q0-1567313402162)(…/…/_images/netty/20181214132452.png)]
通过ChannlHandlerContext的write()方法
6.3.2 ChannelHandler和ChannelHanderContext的高级用法
通过 调用ChannelHandlerContext上的pipeline()方法来获得被封闭的ChannelPipeline的引用。这使得运行时得以操作ChannelPipeline的ChannelHandler,依次我们可以实现比价复杂的内容。
因为一个ChannelHandler 可以从属于多个ChannelPipeline,所以它也可以绑定到多
个ChannelHandlerContext 实例。对于这种用法指在多个ChannelPipeline 中共享同一
个ChannelHandler,对应的ChannelHandler 必须要使用@Sharable 注解标注;否则,
试图将它添加到多个ChannelPipeline 时将会触发异常
6.4 异常处理
6.4.1 处理出站数据异常
- ChannelHandler.exceptionCaught()的默认实现是简单地将当前异常转发给ChannelPipeline 中的下一个ChannelHandler;
- 如果异常到达了ChannelPipeline 的尾端,它将会被记录为未被处理;
- 要想定义自定义的处理逻辑,你需要重写exceptionCaught()方法。然后你需要决定
是否需要将该异常传播出去。
6.4.2 处理出站数据异常
用于处理出站操作中的正常完成以及异常的选项,都基于以下的通知机制。
- 每个出站操作都将返回一个ChannelFuture。注册到ChannelFuture 的Channel-
FutureListener 将在操作完成时被通知该操作是成功了还是出错了。 几乎所有的ChannelOutboundHandler 上的方法都会传入一个ChannelPromise
的实例。作为ChannelFuture 的子类,ChannelPromise 也可以被分配用于异步通
知的监听器。但是,ChannelPromise 还具有提供立即通知的可写方法:ChannelPromise setSuccess();
Chan nelPromise setFailure(Throwable cause);
添加ChannelFutureListener 只需要调用ChannelFuture 实例上的addListener(ChannelFutureListener)方法,并且有两种不同的方式可以做到这一点。其中最常用的方式是,调用出站操作(如write()方法)所返回的ChannelFuture 上的addListener()方法。第二种方式是将ChannelFutureListener 添加到即将作为参数传递给ChannelOutboundHandler的方法的ChannelPromise。
最后
如果你觉得写的还不错,就关注下公众号呗,关注后,有点小礼物回赠给你。
你可以获得5000+电子书,java,springCloud,adroid,python等各种视频教程,IT类经典书籍,各种软件的安装及破解教程。
希望一块学习,一块进步!
还没有评论,来说两句吧...