ChannelInboundHandlerAdapter 与 SimpleChannelInboundHandler 功能详解
SimpleChannelInboundHandler
【类的关系】:如下就是两个类的声明,SimpleChannelInboundHandler是继承 ChannelInboundHandlerAdapter的。也就是说SimpleChannelInboundHandler 也拥有 ChannelInboundHandlerAdapter的方法。
1 //ChannelInboundHandlerAdapter 类
2 ChannelInboundHandlerAdapter extends ChannelHandlerAdapter implements ChannelInboundHandler
3 //SimpleChannelInboundHandler 类
4 SimpleChannelInboundHandler<I> extends ChannelInboundHandlerAdapter
继承关系图: 
SimpleChannelInboundHandler的 channelRead相比 ChannelInboundHandlerAdapter而言,主要做了类型匹配以及用完之后释放指向保存该消息的 ByteBuf的内存引用。这里提供了一个模板,作用是把处理逻辑不变的内容写好在channelRead(ctx,msg) 中,并且在里面调用 channelRead0 ,这样变化的内容通过抽象方法实现传递到子类中去了(在Netty5中channelRead0已被重命名为 messageReceived)。
1 public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {
2 boolean release = true;
3
4 try {
5 if (this.acceptInboundMessage(msg)) {
6 this.channelRead0(ctx, msg);
7 } else {
8 release = false;
9 ctx.fireChannelRead(msg);
10 }
11 } finally {
12 if (this.autoRelease && release) {
13 ReferenceCountUtil.release(msg);
14 }
15 }
16 }
17
18 protected abstract void channelRead0(ChannelHandlerContext var1, I var2) throws Exception;
ChannelInboundHandlerAdapter
相比之下,ChannelInboundHandlerAdapter好像一无是处,毕竟他要自己处理资源的释放,例如如下的调用:buf.release();如果说 channelRead都是同步操作的话,SimpleChannelInboundHandler是不错的选择,如果操作是异步的话,那他的逻辑就有点麻烦了,例如你把数据交给另外的线程处理了,还没处理就会释放了 。这里必须说明一个问题,他的回收和 jvm的垃圾回收还不完全是一回事。netty是自己做了引用计数的操作。 buf.refCnt(); 通过上面的 api就可以获取到计数的个数。具体的引用计数的部分,不知道也不影响 netty的学习,这个点后面具体再说。ChannelInboundHandlerAdapter 处理自由的优点也就提现出来了,可以更好的处理更多的特定场景。
那该方法是什么时候释放资源的呢?当 writeAndFlush() 方法被调用时才被释放,我们点进去源码验证一下:
1 public ChannelFuture writeAndFlush(Object msg, ChannelPromise promise) {
2 if (msg == null) {// msg不能为空
3 throw new NullPointerException("msg");
4 }
5 if (isNotValidPromise(promise, true)) {
6 ReferenceCountUtil.release(msg);// 释放资源(保存消息的ByteBuf)
7 // cancelled
8 return promise;
9 }
10 write(msg, true, promise);// 异步写操作
11 return promise;
12 }
上面源码中,最后资源是通过 ReferenceCountUtil来释放的。也就是说,当我们需要释放 ByteBuf相关内存的时候,也可以使用 ReferenceCountUtil.release()。ReferenceCountUtil 底层实现是 ReferenceCounted ,当新的对象初始化的时候计数为1,retain() 方法被调用时引用计数加1,release()方法被调用时引用计数减1,当计数减少到0的时候会被显示清除,再次访问被清除的对象会出现访问冲突(这里想起了JVM判断对象是否存活的引用计数算法)。ReferenceCountUtil.release源码如下:
1 public static boolean release(Object msg) {
2 if (msg instanceof ReferenceCounted) {
3 return ((ReferenceCounted) msg).release();// Decreases the reference count by 1
4 }
5 return false;
6 }
SimpleChannelInboundHandler 和 ChannelInboundHandlerAdapter区别
在客户端的业务 Handler继承的是 SimpleChannelInboundHandler,而在服务器端继承的是 ChannelInboundHandlerAdapter。最主要的区别就是 SimpleChannelInboundHandler在接收到数据后会自动 release掉数据占用的 Bytebuffer资源(自动调用Bytebuffer.release())。而为何服务器端不能用呢,因为我们想让服务器把客户端请求的数据发送回去,而 write() 操作是异步的,而服务器端有可能在channelRead方法返回后还没有写完数据,因此不能让它自动release。SimpleChannelInboundHandler 是有泛型参数的。可以指定一个具体的类型参数,通过 decoder配合使用,非常方便。ChannelInboundHandlerAdapter 则是直接操作 byte数组的。
SimpleChannelInboundHandler 的好处是可以处理不同的类型对象,并且可以做释放。ChannelInboundHandlerAdapter的好处则是更自由,在异步的场景下更适合。
ChannelInboundHandlerAdapter 注意事项
用户自定义了一个 Handler类,代码如下:
1 @Override
2 public void channelRegistered(ChannelHandlerContext ctx) throws Exception {
3 this.ctx = ctx;
4 }
5
6 @Override
7 public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {
8 super.channelRead(ctx, msg);
9 log.debug("thread.name={}", Thread.currentThread().getName());
10
11 ByteBuf in = (ByteBuf) msg;
12
13 String readStr = in.toString(CharsetUtil.UTF_8);
14 log.debug("Server received: {}", readStr);
15
16 log.debug("release msg");
17 ReferenceCountUtil.release(msg);
18 }
19
20 @Override
21 public void channelReadComplete(ChannelHandlerContext ctx) throws Exception {
22 ctx.flush();
23 }
24
25 @Override
26 public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception {
27 cause.printStackTrace();
28 ctx.close();
29 }
运行时出现了如下堆栈异常:
io.netty.util.IllegalReferenceCountException: refCnt: 0
at io.netty.buffer.AbstractByteBuf.ensureAccessible(AbstractByteBuf.java:1173)
at io.netty.buffer.AbstractByteBuf.checkIndex(AbstractByteBuf.java:1119)
at io.netty.buffer.UnpooledUnsafeDirectByteBuf.internalNioBuffer(UnpooledUnsafeDirectByteBuf.java:385)
at io.netty.buffer.ByteBufUtil.decodeString(ByteBufUtil.java:568)
at io.netty.buffer.AbstractByteBuf.toString(AbstractByteBuf.java:979)
at io.netty.buffer.AbstractByteBuf.toString(AbstractByteBuf.java:974)
at com.spy.apollo.netty.demo.demo02_biz_logic.ServerBizHandler.channelRead(ServerBizHandler.java:50)
异常说是引用计数 refCnf =0,也就是没有被引用,因此报错;常规的 channelRead中消息读取完毕是要立即释放当前消息的引用计数即(减一操作)如下代码操作。但是其他 Handler也去释放时,发现没有引用,就会报错。
ReferenceCountUtil.release(msg);
分析:通过调试代码发现根源就在 super.channelRead(ctx, msg);这个函数。其实 ChannelInboundHandlerAdapter 是提供了一种默认实现,子类如果要继承,需要覆盖父类中的方法,并且不需要调用 super.xxxxMethod()。源码如下:意思是此实现只是将操作转发到下一个 channelhandler。子类可以重写方法实现来改变这一点。
1 /**
2 * <p>
3 * This implementation just forward the operation to the next {@link ChannelHandler} in the
4 * {@link ChannelPipeline}. Sub-classes may override a method implementation to change this.
5 * </p>
6 */
7 public class ChannelInboundHandlerAdapter extends ChannelHandlerAdapter implements ChannelInboundHandler {
ChannelInboundHandlerAdapter 与 SimpleChannelInboundHandler 功能详解的更多相关文章
- iOS之UI--使用SWRevealViewController实现侧边菜单功能详解实例
使用SWRevealViewController实现侧边菜单功能详解 下面通过两种方法详解SWRevealViewController实现侧边菜单功能: 1.使用StoryBoard实现 2.纯代 ...
- SVN功能详解
SVN功能详解 TortoiseSVN是windows下其中一个非常优秀的SVN客户端工具.通过使用它,我们可以可视化的管理我们的版本库.不过由于它只是一个客户端,所以它不能对版本库进行权限管理. ...
- 转载]IOS LBS功能详解[0](获取经纬度)[1](获取当前地理位置文本 )
原文地址:IOS LBS功能详解[0](获取经纬度)[1](获取当前地理位置文本作者:佐佐木小次郎 因为最近项目上要用有关LBS的功能.于是我便做一下预研. 一般说来LBS功能一般分为两块:一块是地理 ...
- UIViewController中各方法调用顺序及功能详解
UIViewController中各方法调用顺序及功能详解 UIViewController中loadView, viewDidLoad, viewWillUnload, viewDidUnload, ...
- MySQL的用户密码过期功能详解
MySQL的用户密码过期功能详解 作者:chszs,未经博主允许不得转载.经许可的转载需注明作者和博客主页:http://blog.csdn.net/chszs 先说明两个术语. Payment Ca ...
- 【转】 /etc/fstab功能详解
[转] /etc/fstab功能详解 最近去客户现场时,遇到 了一个关于挂载文件/etc/fstab文件的问题,就写了一下/etc/fstab文件的作用一个文件中各个参数的含义.供大家参考有不正确的地 ...
- 在ASP.NET 5应用程序中的跨域请求功能详解
在ASP.NET 5应用程序中的跨域请求功能详解 浏览器安全阻止了一个网页中向另外一个域提交请求,这个限制叫做同域策咯(same-origin policy),这组织了一个恶意网站从另外一个网站读取敏 ...
- .NET ORM框架 SqlSuagr4.0 功能详解与实践【开源】
SqlSugar 4.0 ORM框架的优势 为了未来能够更好的支持多库分布式的存储,并行计算等功能,将SqlSugar3.x全部重写,现有的架构可以轻松扩展多库. 源码下载: https://gith ...
- SNS社交系统“ThinkSNS V4.6”活动应用功能详解及应用场景举例
sns社交系统ThinkSNS目前拥有功能:朋友圈(微博).微吧(论坛).频道.积分商城.IM即时聊天.直播.问答.活动.资讯(CMS).商城.广场.找人.搜索.评论.点赞.转发.分享.话题.积分.充 ...
- Jmeter常用功能详解
嘻嘻,忙碌的一周,马上就到周四了~明天就是周五了,可以去嗨了! 这几天正式成立了一个微信订阅号,旨在免费帮助需要入门软件测试的小白! 各位走过路过的亲,欢迎订阅哦:扫描二维码即可订阅
随机推荐
- CIC滤波器
CIC滤波器是滑动平均滤波器的非常高效的迭代实现,只需要一个减法和一个加法,而滑动平均需要N-1个加法. cic滤波器相当于一个梳状滤波器y(n)=x(n)-x(n-D),H(z)=1-z-D,和一个 ...
- 20211306 实验一《Python程序设计》实验报告
202111306 丁文博 第一次实验报告 课程:<Python程序设计> 班级: 2113 姓名: 丁文博 学号:20211306 实验教师:王志强 实验日期:2022年3月24日 必修 ...
- 等级保护2.0 三级-Linux 测评指导书
等级保护2.0 三级-Linux 测评指导书 1.1安全计算环境 1.1.1身份鉴别 1.1.2访问控制 1.1.4入侵防范 1.1.5恶意代码防范 ...
- Windows10 Docker报错 ERROR: Couldn't connect to Docker daemon - you might need to run `docker-machine start default`.
问题描述:Windows10 家庭版 docker确认已启动,但是执行 docker-compose up -d 时报错,提示需启动docker: ERROR: Couldn't connect to ...
- 执行Maven的test命令报错
参考网址:https://blog.csdn.net/weixin_46688566/article/details/126470742 解决方案 在pom.xml文件中加入以下依赖: <plu ...
- anaconda的环境变量
参考: (40条消息) Anaconda 环境变量手动设置(详细)_一夜星尘的博客-CSDN博客_anaconda环境变量手动设置
- web后端之过滤器
1直接java搞定 2把@WebFilter()去掉,配置web.xml文件.
- web之jquery
jquery 插件 下载解压插件 加载jQuery 加载插件 插件样式 按照使用例子给元素添加类名,id,函数.
- git在linux安装步骤详解!!
linux上安装,以centos 7.x为例 yum命令安装 yum install gityum install 安装的git不是最新版本,如需最新版本需要自行编译 到下面的网站下载合适的版本 ht ...
- 正确理解RestFul 接口
一.REST# REST,即Representational State Transfer的缩写,翻译过来就是"表现层状态转化".不得不承认,我在刚开始看到这个名词的时候是一脸懵逼 ...