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. 判断用户名是否存在 后…...
脑机新手指南(八):OpenBCI_GUI:从环境搭建到数据可视化(下)
一、数据处理与分析实战 (一)实时滤波与参数调整 基础滤波操作 60Hz 工频滤波:勾选界面右侧 “60Hz” 复选框,可有效抑制电网干扰(适用于北美地区,欧洲用户可调整为 50Hz)。 平滑处理&…...
Auto-Coder使用GPT-4o完成:在用TabPFN这个模型构建一个预测未来3天涨跌的分类任务
通过akshare库,获取股票数据,并生成TabPFN这个模型 可以识别、处理的格式,写一个完整的预处理示例,并构建一个预测未来 3 天股价涨跌的分类任务 用TabPFN这个模型构建一个预测未来 3 天股价涨跌的分类任务,进行预测并输…...
React19源码系列之 事件插件系统
事件类别 事件类型 定义 文档 Event Event 接口表示在 EventTarget 上出现的事件。 Event - Web API | MDN UIEvent UIEvent 接口表示简单的用户界面事件。 UIEvent - Web API | MDN KeyboardEvent KeyboardEvent 对象描述了用户与键盘的交互。 KeyboardEvent - Web…...
sqlserver 根据指定字符 解析拼接字符串
DECLARE LotNo NVARCHAR(50)A,B,C DECLARE xml XML ( SELECT <x> REPLACE(LotNo, ,, </x><x>) </x> ) DECLARE ErrorCode NVARCHAR(50) -- 提取 XML 中的值 SELECT value x.value(., VARCHAR(MAX))…...
css的定位(position)详解:相对定位 绝对定位 固定定位
在 CSS 中,元素的定位通过 position 属性控制,共有 5 种定位模式:static(静态定位)、relative(相对定位)、absolute(绝对定位)、fixed(固定定位)和…...
C++中string流知识详解和示例
一、概览与类体系 C 提供三种基于内存字符串的流,定义在 <sstream> 中: std::istringstream:输入流,从已有字符串中读取并解析。std::ostringstream:输出流,向内部缓冲区写入内容,最终取…...
C# SqlSugar:依赖注入与仓储模式实践
C# SqlSugar:依赖注入与仓储模式实践 在 C# 的应用开发中,数据库操作是必不可少的环节。为了让数据访问层更加简洁、高效且易于维护,许多开发者会选择成熟的 ORM(对象关系映射)框架,SqlSugar 就是其中备受…...
OpenLayers 分屏对比(地图联动)
注:当前使用的是 ol 5.3.0 版本,天地图使用的key请到天地图官网申请,并替换为自己的key 地图分屏对比在WebGIS开发中是很常见的功能,和卷帘图层不一样的是,分屏对比是在各个地图中添加相同或者不同的图层进行对比查看。…...
3-11单元格区域边界定位(End属性)学习笔记
返回一个Range 对象,只读。该对象代表包含源区域的区域上端下端左端右端的最后一个单元格。等同于按键 End 向上键(End(xlUp))、End向下键(End(xlDown))、End向左键(End(xlToLeft)End向右键(End(xlToRight)) 注意:它移动的位置必须是相连的有内容的单元格…...
Docker 本地安装 mysql 数据库
Docker: Accelerated Container Application Development 下载对应操作系统版本的 docker ;并安装。 基础操作不再赘述。 打开 macOS 终端,开始 docker 安装mysql之旅 第一步 docker search mysql 》〉docker search mysql NAME DE…...
