netty4报错:io.netty.util.IllegalReferenceCountException: refCnt: 0, decrement: 1
背景:netty执行链中有一串自定义的handler,目前我想要在中间再加上一个pingPonghandler来进行控制帧的处理,从而避免ng的读写超时(客户要求,与他们建立的通道一直连接,不进行断连,从而需要考虑ng的问题);
当我添加如下代码后报错:io.netty.util.IllegalReferenceCountException: refCnt: 0, decrement: 1,ByteBuf
的引用计数refCnt为0;
import io.netty.buffer.Unpooled;
import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.SimpleChannelInboundHandler;
import io.netty.handler.codec.http.websocketx.PongWebSocketFrame;
import io.netty.handler.codec.http.websocketx.PingWebSocketFrame;
import io.netty.handler.codec.http.websocketx.WebSocketFrame; public class PingPongHandler extends SimpleChannelInboundHandler<WebSocketFrame> { @Override protected void channelRead0(ChannelHandlerContext ctx, WebSocketFrame frame) throws Exception { if (frame instanceof PingWebSocketFrame) { // 接收到PING帧 System.out.println("Received PING frame"); // 发送PONG帧作为响应 ctx.channel().writeAndFlush(new PongWebSocketFrame(Unpooled.buffer(0))); } else if (frame instanceof PongWebSocketFrame) { // 接收到PONG帧 System.out.println("Received PONG frame"); } else { // 其他类型的帧,继续传递给下一个处理器 ctx.fireChannelRead(frame); } }
}
ByteBuf echoMsg = frame.content();
echoMsg.refCnt(); 获取引用计数
解决办法1:(新手不推荐,最好还是使用netty的自动释放逻辑,避免处理不当)
echoMsg.retain(); //手动进行引用计数加1,并在使用完成之后释放调
解决方法2:关闭SimpleChannelInboundHandler的自动释放(推荐)
package com.iflytek.iptv.websocket;import io.netty.buffer.Unpooled;
import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.SimpleChannelInboundHandler;
import io.netty.handler.codec.http.websocketx.PongWebSocketFrame;
import io.netty.handler.codec.http.websocketx.PingWebSocketFrame;
import io.netty.handler.codec.http.websocketx.WebSocketFrame;public class PingPongHandler extends SimpleChannelInboundHandler<WebSocketFrame> {// 构造函数中设置autoRelease为falsepublic PingPongHandler() {super(false);}@Overrideprotected void channelRead0(ChannelHandlerContext ctx, WebSocketFrame frame) throws Exception {if (frame instanceof PingWebSocketFrame) {// 接收到PING帧System.out.println("Received PING frame");// 发送PONG帧作为响应ctx.channel().writeAndFlush(new PongWebSocketFrame(Unpooled.buffer(0)));} else if (frame instanceof PongWebSocketFrame) {// 接收到PONG帧System.out.println("Received PONG frame");} else {// 其他类型的帧,继续传递给下一个处理器ctx.fireChannelRead(frame);}}
}
解决方法3:继承ChannelInboundHandlerAdapter ,默认不释放(推荐)
package com.iflytek.iptv.websocket;import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.ChannelInboundHandlerAdapter;
import io.netty.handler.codec.http.websocketx.PongWebSocketFrame;
import io.netty.handler.codec.http.websocketx.PingWebSocketFrame;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;public class PingPongHandler extends ChannelInboundHandlerAdapter {private static final Logger log = LoggerFactory.getLogger(PingPongHandler1.class);@Overridepublic void channelRead(ChannelHandlerContext ctx, Object frame) throws Exception {if (frame instanceof PingWebSocketFrame) {// 接收到PING帧log.info("Received ping frame, connection is alive");// 发送PONG帧作为响应
// ctx.channel().writeAndFlush(new PongWebSocketFrame(Unpooled.buffer(0)));} else if (frame instanceof PongWebSocketFrame) {// 接收到PONG帧log.info("Received pong frame, connection is alive");} else {// 其他类型的帧,继续传递给下一个处理ctx.fireChannelRead(frame);}}
}
问题分析:
在Netty中,ByteBuf
是一个字节容器,它支持多种数据读写操作,并且其内部使用引用计数(refCnt)来管理资源的生命周期。引用计数用于确保资源(如内存)在不再需要时能够被适当释放,防止内存泄漏。
当你提到继承 SimpleChannelInboundHandler<WebSocketFrame>
和 ChannelInboundHandlerAdapter
并使用 ctx.fireChannelRead(frame)
时,ByteBuf
的引用计数表现不同,这主要是由于 SimpleChannelInboundHandler
和 ChannelInboundHandlerAdapter
处理 ByteBuf
的方式有所区别。
SimpleChannelInboundHandler
SimpleChannelInboundHandler<T>
是一个方便的类,它专门为处理特定类型的入站消息而设计。它会在消息传递到下一个 ChannelHandler
之前自动释放(release()
)传入的消息对象(如果消息实现了 ReferenceCounted
接口,如 ByteBuf
)。这是因为它假设在 channelRead0(ChannelHandlerContext ctx, T msg)
方法中处理完消息后,当前处理器不需要再保留对该消息的引用。
因此,当你在 SimpleChannelInboundHandler
的 channelRead0
方法中调用 ctx.fireChannelRead(frame)
(这里的 frame
是 WebSocketFrame
的一个实例,而 WebSocketFrame
内部通常包含 ByteBuf
),由于 SimpleChannelInboundHandler
的设计,Netty 会在将消息传递给下一个处理器之前自动调用 release()
。这就是为什么你看到引用计数减少的原因。
ChannelInboundHandlerAdapter
相反,ChannelInboundHandlerAdapter
是一个更通用的基类,它不假设你会在方法执行后立即释放消息。因此,当你在 ChannelInboundHandlerAdapter
的 channelRead
方法中调用 ctx.fireChannelRead(frame)
时,Netty 不会自动调用 release()
。这意呀着你需要自己管理 ByteBuf
的生命周期,确保在不再需要时调用 release()
,否则可能导致内存泄漏。
应该选择哪个?
- 如果你处理的是实现了
ReferenceCounted
接口的对象(如ByteBuf
),并且你的处理器在处理完消息后不再需要它,那么SimpleChannelInboundHandler
是一个不错的选择,因为它可以自动管理引用计数。 - 如果你需要更细粒度的控制,比如你计划在多个处理器之间共享
ByteBuf
,或者你的处理器只是将消息传递给下一个处理器而不立即处理它,那么ChannelInboundHandlerAdapter
可能是更好的选择。但是,你需要确保自己正确地管理引用计数,避免内存泄漏。
结论
不是所有的中间处理器都必须继承 ChannelInboundHandlerAdapter
。如果你处理的消息类型是特定的,并且你可以接受 SimpleChannelInboundHandler
的自动释放行为,那么使用它可能更方便。然而,如果你需要更复杂的处理逻辑或消息共享,那么 ChannelInboundHandlerAdapter
将提供更多的灵活性。重要的是要根据你的具体需求来选择适当的处理器类。
相关文章:
netty4报错:io.netty.util.IllegalReferenceCountException: refCnt: 0, decrement: 1
背景:netty执行链中有一串自定义的handler,目前我想要在中间再加上一个pingPonghandler来进行控制帧的处理,从而避免ng的读写超时(客户要求,与他们建立的通道一直连接,不进行断连,从而需要考虑n…...
2022年汽车软件行业产业细分及发展趋势分析
2022年汽车软件行业产业细分及发展趋势分析 连接 2022年汽车软件行业产业细分及发展趋势分析...
如何通过变更让 PostgreSQL 翻车
在开发应用程序和维护其后台数据库集群的过程中,我们经常会遇到实践与理论、开发环境与生产环境之间的差异。其中一个典型的例子就是变更数据库中的列类型。 对于在 PostgreSQL(及其他符合 SQL 标准的系统)中变更列类型的常规操作࿰…...

MySQL表涉及规范
MySQL表设计规范是为了确保数据库表结构的合理性、可读性和可维护性。以下是一些建议和规范: 1.使用InnoDB存储引擎:InnoDB存储引擎提供了事务支持、行级锁定和外键约束,有助于提高数据的完整性和性能。 2.表名和字段名命名规范:…...

服务器Ubuntu22.04系统 使用dcocker部署安装ollama和搭配open_webui使用
服务器Ubuntu22.04系统 使用dcocker部署安装ollama和搭配open_webui使用 一、ubuntu和docker基本环境配置 1.更新包列表: 打开终端,输入以下命令: sudo apt-get updatesudo apt upgrade更新时间较长,请耐心等待 2. 安装docke…...

代理模式Proxy
一、代理模式(Proxy) 1.代理模式的定义 代理模式给某一个对象提供一个代理对象,并由代理对象控制对真实对象的访问,起到对代理对象已有功能的增强 通俗的来讲代理模式就是我们生活中常见的中介。 2.作用 中介隔离作用&#x…...

C++ 设计模式——抽象工厂模式
抽象工厂模式 抽象工厂模式 抽象工厂模式主要组成部分代码实现抽象工厂模式模式的 UML 图抽象工厂模式 UML 图解析优点和缺点适用场景 抽象工厂模式提供一个接口,用于创建一系列相关或相互依赖的对象,而无需指定它们的具体类。它通常用于需要创建多个产品…...

《亿级流量系统架构设计与实战》第十一章 Timeline Feed服务
Timeline Feed服务 一、概述1、分类2、功能 二、设计原理1、拉模式与用户发件箱2、推模式与用户收件箱3、推拉模式结合 三、关键技术1、内容与用户收件箱的交互(推模式)2、推送拆分子任务3、收件箱模型设计 内容总结自《亿级流量系统架构设计与实战》 一…...

氙灯老化试验箱试验机
氙灯老化试验箱,采用6.5KW大功率的精密水冷式氙灯,曝晒面积达到了6500cm2 功能强大,测试结果可靠 ◆ 满足国内外所有氙灯测试标准要求。 ◆ 采用氙灯灯管及滤光器组件,保证试验数据的可比性和重现性。 ◆ 自动旋转式三层鼓型样板架…...

【Qt】常用控件QRadioButton
常用控件QRadioButton QRadioButton是单选按钮,可以在多个选项中选择一个。 作为QAbstractButton和QWidget的子类,其属性和用法,对于QRadioButton同样适用。 属性说明 checkable 是否能选中 checked 是否已经被选中. checkable 是 checked…...

Mysql 离线版下载安装-(详细版)
Mysql 离线版下载安装-(详细版) 文章目录 Mysql 离线版下载安装-(详细版)1.0 下载地址2.0 解压到本地2.0.1 配置环境变量2.0.2 新建mysql配置文件ini2.0.3使用管理员启动 cmd 3.0 初始化密码忘记了4.0 修改初始化密码5.0 使用可视化工具登录Mysql 1.0 下载地址 地址࿱…...

Spring Boot和OCR构建车牌识别系统
博客主页: 南来_北往 系列专栏:Spring Boot实战 OCR介绍 OCR(Optical Character Recognition)是光学字符识别技术的缩写,它能够将图像中的文本转换为机器可读和编辑的数字文本格式。这种技术广泛应用于数据输入、文档管理…...

Java-自定义注解中成员变量是Class<?>
在Java中,自定义注解可以包含各种类型的成员变量,包括 Class<?> 类型。这种类型的成员变量 通常用于表示某个类的类型信息。下面我将详细介绍如何定义一个包含 Class<?> 类型成员变量的 自定义注解,并给出一些示例代码。 1. 定义自定义注解 定义一个自定义…...
SX_UNIX套接字通信_15
UNIX套接字通信的优势: UNIX套接字通信常用于一个项目中的进程之间通信,UNIX提供了与网络套接字相似的特性,但是避免了网络延迟,提高了性能,但是它只能在同一台机器上使用,无法跨越网络的进程间通信 实例&…...

JS模块化总结 | CommonJS、ES6
BV13W42197jR 个人笔记 目录 JS模块化基础知识1. 概述1.1 什么是模块化1.2 为什么需要模块化? 2 模块化规范3 导入&导出4 CommonJS规范4.1 初步体验4.2 导出数据4.3 导入数据4.4 扩展理解4.5 浏览器端运行 5 ES6模块化规范5.1 初步体验5.2 Node中运行ES65.3 导出数据①分别…...
25考研计算机组成原理复习·3.5高速缓冲存储器
高速缓冲存储器Cache 工作原理:将某些主存块复制到Cache中,缓和CPU与主存之间的速度矛盾局部性原理 时间局部性:现在访问的地址,不久之后也很可能被再次访问空间局部性:现在访问的地址,其附近的地址也很可…...

餐厅管理系统
目录 一、 系统简介 1.1需求分析 1.2 编程环境与工具 二、 系统总体设计 2.1 系统的功能模块图。 2.2 各功能模块简介。 三、 主要业务流程 (1)用户及管理员登录流程图 (2)信息添加流程 (3…...
杭州百腾教育科技 TiDB 6.5 to 7.5 升级记录
作者: reAsOn2010 原文来源: https://tidb.net/blog/612103f3 背景 使用 TiDB 作为我们的全量数据库已经有六七年了,当时还是 2.0 版本。早期TiDB的迭代和新特性的发布对于实际使用的影响还是很大的,所以从那个时候开始就有每…...
Redis的缓存穿透、击穿、雪崩
目录 缓存穿透 定义: 解决方法: 缓存击穿 定义: 解决方案: 缓存雪崩 定义: 解决方案: 缓存穿透、缓存击穿和缓存雪崩的区别 缓存穿透 定义: 查询一个不存在的数据,数据库未…...

【Django开发】前后端分离django美多商城项目第1篇:欢迎来到美多 项目主要页面介绍【附代码文档】
本教程的知识点为: 项目准备 项目准备 配置 1. 修改settings/dev.py 文件中的路径信息 2. INSTALLED_APPS 3. 数据库 用户部分 图片 1. 后端接口设计: 视图原型 2. 具体视图实现 用户部分 使用Celery完成发送 判断帐号是否存在 1. 判断用户名是否存在 后…...

Spark 之 入门讲解详细版(1)
1、简介 1.1 Spark简介 Spark是加州大学伯克利分校AMP实验室(Algorithms, Machines, and People Lab)开发通用内存并行计算框架。Spark在2013年6月进入Apache成为孵化项目,8个月后成为Apache顶级项目,速度之快足见过人之处&…...
oracle与MySQL数据库之间数据同步的技术要点
Oracle与MySQL数据库之间的数据同步是一个涉及多个技术要点的复杂任务。由于Oracle和MySQL的架构差异,它们的数据同步要求既要保持数据的准确性和一致性,又要处理好性能问题。以下是一些主要的技术要点: 数据结构差异 数据类型差异ÿ…...

【SQL学习笔记1】增删改查+多表连接全解析(内附SQL免费在线练习工具)
可以使用Sqliteviz这个网站免费编写sql语句,它能够让用户直接在浏览器内练习SQL的语法,不需要安装任何软件。 链接如下: sqliteviz 注意: 在转写SQL语法时,关键字之间有一个特定的顺序,这个顺序会影响到…...
今日科技热点速览
🔥 今日科技热点速览 🎮 任天堂Switch 2 正式发售 任天堂新一代游戏主机 Switch 2 今日正式上线发售,主打更强图形性能与沉浸式体验,支持多模态交互,受到全球玩家热捧 。 🤖 人工智能持续突破 DeepSeek-R1&…...

QT: `long long` 类型转换为 `QString` 2025.6.5
在 Qt 中,将 long long 类型转换为 QString 可以通过以下两种常用方法实现: 方法 1:使用 QString::number() 直接调用 QString 的静态方法 number(),将数值转换为字符串: long long value 1234567890123456789LL; …...
安卓基础(aar)
重新设置java21的环境,临时设置 $env:JAVA_HOME "D:\Android Studio\jbr" 查看当前环境变量 JAVA_HOME 的值 echo $env:JAVA_HOME 构建ARR文件 ./gradlew :private-lib:assembleRelease 目录是这样的: MyApp/ ├── app/ …...
比较数据迁移后MySQL数据库和OceanBase数据仓库中的表
设计一个MySQL数据库和OceanBase数据仓库的表数据比较的详细程序流程,两张表是相同的结构,都有整型主键id字段,需要每次从数据库分批取得2000条数据,用于比较,比较操作的同时可以再取2000条数据,等上一次比较完成之后,开始比较,直到比较完所有的数据。比较操作需要比较…...

stm32wle5 lpuart DMA数据不接收
配置波特率9600时,需要使用外部低速晶振...

Axure 下拉框联动
实现选省、选完省之后选对应省份下的市区...

从零开始了解数据采集(二十八)——制造业数字孪生
近年来,我国的工业领域正经历一场前所未有的数字化变革,从“双碳目标”到工业互联网平台的推广,国家政策和市场需求共同推动了制造业的升级。在这场变革中,数字孪生技术成为备受关注的关键工具,它不仅让企业“看见”设…...