深入理解SimpleChannelInboundHandler
因项目需要,需要了解 Netty 这款号称 "高性能Java网络编程" 框架。拿起一本《Netty In Action》开始研究,在第2章的例子中,发现 Echo 服务端使用的ChannelHandler是 ChannelInboundHandlerAdapter ,而 Echo 客户端使用的却是 SimpleChannelInboundHandler 。一脸茫然,不知所措,只能点进去看各自的实现原理。
一.SimpleChannelInboundHandler
首先看到的是 SimpleChannelInboundHandler 继承自 ChannelInboundHandlerAdapter。
public abstract class SimpleChannelInboundHandler<I> extends ChannelInboundHandlerAdapter{ ... }
有图有真相:
既然是继承关系,也就是说,"你有的我也有,你没有的我还有。" 那么 SimpleChannelInboundHandler 里面肯定重写或者新增了 ChannelInboundHandlerAdapter 里面的方法功能 - channelRead0 和 channelRead()。
protected abstract void channelRead0(ChannelHandlerContext ctx, I msg) throws Exception;
这里只提供了一个模板,作用是把处理逻辑不变的内容写好在 channelRead(ctx,msg) 中,并且在里面调用 channelRead0 ,这样变化的内容通过抽象方法实现传递到子类中去了(在Netty5中channelRead0已被重命名为messageReceived)。
@Override// 继承了 ChannelInboundHandlerAdapter#channelReadpublic void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {boolean release = true;try {if (acceptInboundMessage(msg)) {@SuppressWarnings("unchecked")I imsg = (I) msg;channelRead0(ctx, imsg);// 模板方法,抽出可变的部分,具体实现在子类中} else {release = false;ctx.fireChannelRead(msg);}} finally {if (autoRelease && release) {ReferenceCountUtil.release(msg);// 释放资源(保存消息的ByteBuf)}}}
为什么这样做?看看《Netty In Action》的原话:
在客户端,当 channelRead0() 方法完成时,你已经有了传入消息,并且已经处理完它了。当该方法返回时,SimpleChannelInboundHandler负责释放指向保存该消息的ByteBuf的内存引用。
那为什么服务端不需要这样处理呢?
在EchoServerHandler中,你仍然需要将传入消息回送给发送者,而 write() 操作是异步的,直到 channelRead() 方法返回后可能仍然没有完成。为此,EchoServerHandler扩展了 ChannelInboundHandlerAdapter ,其在这个时间点上不会释放消息。
啥意思,我的理解是 ChannelInboundHandlerAdapter 不会像 SimpleChannelInboundHandler 一样在 channelRead() 里面释放资源,而是在收到消息处理完成的事件时,才会释放资源,看下面的代码就能理解了。
二.ChannelInboundHandlerAdapter
EchoServerHandler#channelReadComplete,这是一个EchoServer小例子:
public void channelReadComplete(ChannelHandlerContext ctx)throws Exception {//将未决消息冲刷到远程节点,并且关闭该 Channelctx.writeAndFlush(Unpooled.EMPTY_BUFFER).addListener(ChannelFutureListener.CLOSE);}
消息在 channelReadComplete() 方法中,当 writeAndFlush() 方法被调用时才被释放,我们点进去源码验证一下:
AbstractChannelHandlerContext#writeAndFlush,如下所示,最后的资源是在 writeAndFlush() 中被释放的。
public ChannelFuture writeAndFlush(Object msg) {return writeAndFlush(msg, newPromise());// 跳转到另外一个重载的方法中}public ChannelFuture writeAndFlush(Object msg, ChannelPromise promise) {if (msg == null) {// msg不能为空throw new NullPointerException("msg");}if (isNotValidPromise(promise, true)) {ReferenceCountUtil.release(msg);// 释放资源(保存消息的ByteBuf)// cancelledreturn promise;}write(msg, true, promise);// 异步写操作return promise;}
三.扩展 - ReferenceCountUtil
上面的源码中,最后资源是通过 ReferenceCountUtil 来释放的。也就是说,当我们需要释放ByteBuf相关内存的时候,也可以使用 ReferenceCountUtil#release()。
ReferenceCountUtil 底层实现是 ReferenceCounted ,当新的对象初始化的时候计数为1,retain() 方法被调用时引用计数加1,release()方法被调用时引用计数减1,当计数减少到0的时候会被显示清除,再次访问被清除的对象会出现访问冲突(这里想起了JVM判断对象是否存活的引用计数算法)。
ReferenceCountUtil#release:
public static boolean release(Object msg) {if (msg instanceof ReferenceCounted) {return ((ReferenceCounted) msg).release();// Decreases the reference count by 1}return false;}
四.参考
Netty进阶之路第三章:Netty内存池泄漏疑云案例
深入理解SimpleChannelInboundHandler相关推荐
- 关于Netty的一些理解、实践与陷阱
核心概念的理解 Netty对于网络层进行了自己的抽象,用Channel表示连接,读写就是Channel上发生的事件,ChannelHandler用来处理这些事件,ChannelPipeline基于un ...
- 深入理解Netty编解码、粘包拆包、心跳机制
点赞再看,养成习惯,公众号搜一搜[一角钱技术]关注更多原创技术文章. 本文 GitHub org_hejianhui/JavaStudy 已收录,有我的系列文章. 前言 BIO .NIO .AIO 总 ...
- 通用解题法——回溯算法(理解+练习)
积累算法经验,积累解题方法--回溯算法,你必须要掌握的解题方法! 什么是回溯算法呢? 回溯算法实际上一个类似枚举的搜索尝试过程,主要是在搜索尝试过程中寻找问题的解,当发现已不满足求解条件时,就&quo ...
- stream流对象的理解及使用
我的理解:用stream流式处理数据,将数据用一个一个方法去 . (点,即调用) 得到新的数据结果,可以一步达成. 有多种方式生成 Stream Source: 从 Collection 和数组 Co ...
- Linux shell 学习笔记(11)— 理解输入和输出(标准输入、输出、错误以及临时重定向和永久重定向)
1. 理解输入和输出 1.1 标准文件描述符 Linux 系统将每个对象当作文件处理.这包括输入和输出进程.Linux 用文件描述符(file descriptor)来标识每个文件对象.文件描述符是一 ...
- java局部变量全局变量,实例变量的理解
java局部变量全局变量,实例变量的理解 局部变量 可以理解为写在方法中的变量. public class Variable {//类变量static String name = "小明&q ...
- 智能文档理解:通用文档预训练模型
预训练模型到底是什么,它是如何被应用在产品里,未来又有哪些机会和挑战? 预训练模型把迁移学习很好地用起来了,让我们感到眼前一亮.这和小孩子读书一样,一开始语文.数学.化学都学,读书.网上游戏等,在脑子 ...
- 熵,交叉熵,散度理解较为清晰
20210511 https://blog.csdn.net/qq_35455503/article/details/105714287 交叉熵和散度 自己给自己编码肯定是最小的 其他的编码都会比这个 ...
- mapreduce理解_大数据
map:对不同的数据进行同种操作 reduce:按keys 把数据规约到一起 看这篇文章请出去跑两圈,然后泡一壶茶,边喝茶,边看,看完你就对hadoop 与MapReduce的整体有所了解了. [前言 ...
最新文章
- 牛客多校8 - All-Star Game(线段树分治+并查集按秩合并的撤销操作)
- 每日一道算法题--leetcode 746--使用最小花费爬楼梯--python
- 小米开源语音模型 Kaldi-ONNX 转换工具,助力移动端部署!
- Android Studio(五):修改Android Studio项目包名
- java web项目用cookie记住用户名、密码
- java 调用tomcat api,tomcat处理http请求-下
- 主成分分析(PCA)实现代码
- android 编译 libjpeg-turbo,编译Android环境的libjpeg-turbo
- php在线翻译,php调用百度在线翻译api的代码
- 数据采集及预处理——针对“数据”“采集”“预处理”的理解与解析
- Irvue for Mac(苹果壁纸软件)
- 地铁三号线 - 为什么哭的时候总是叫我带娃?
- 测肤API+应用开发,自助打开线上AI测肤
- 星秒入选毕马威(KPMG)汽车科技新锐企业榜
- Python 大作业
- HTTP权威指南------URL与资源
- 将url地址中的编码转汉字
- easyrecovery professional专业版下载 v14.0.0.0 专业版介绍
- 如何让自己在云服务器上部署的进程一直运行而无需一直连接服务器
- [修改 Mysql5.7密码策略]Your password does not satisfy the current policy requirements
热门文章
- 用正则表达式将文字转换成表情图片
- Bayesian Convolution Neural Networks with Bernoulli Approximate Variational Inference
- 如何应对网站流量暴增
- C# 获取枚举类型描述Description值
- 读书笔记-全面薪酬体系设计“6+1”
- 基于单片机的坐姿(提醒)矫正系统设计
- windows自带的复制粘贴历史工具
- Android Framework 包管理子系统(03)应用安装
- html计算圆周长,【实用性程序】弧微分计算圆周长
- (CVE-2014-0160)OpenSSL 心脏出血漏洞