当前位置: 首页 > news >正文

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 将提供更多的灵活性。重要的是要根据你的具体需求来选择适当的处理器类。

http://www.lryc.cn/news/427401.html

相关文章:

  • 2022年汽车软件行业产业细分及发展趋势分析
  • 如何通过变更让 PostgreSQL 翻车
  • MySQL表涉及规范
  • 服务器Ubuntu22.04系统 使用dcocker部署安装ollama和搭配open_webui使用
  • 代理模式Proxy
  • C++ 设计模式——抽象工厂模式
  • 《亿级流量系统架构设计与实战》第十一章 Timeline Feed服务
  • 氙灯老化试验箱试验机
  • 【Qt】常用控件QRadioButton
  • Mysql 离线版下载安装-(详细版)
  • Spring Boot和OCR构建车牌识别系统
  • Java-自定义注解中成员变量是Class<?>
  • SX_UNIX套接字通信_15
  • JS模块化总结 | CommonJS、ES6
  • 25考研计算机组成原理复习·3.5高速缓冲存储器
  • 餐厅管理系统
  • 杭州百腾教育科技 TiDB 6.5 to 7.5 升级记录
  • Redis的缓存穿透、击穿、雪崩
  • 【Django开发】前后端分离django美多商城项目第1篇:欢迎来到美多 项目主要页面介绍【附代码文档】
  • 【软件造价咨询】信息化项目预算评审看什么?
  • 第37讲:Cephfs文件系统的正确使用姿势
  • 单片机烧录
  • mysql实现分布式锁
  • MySQL快速使用
  • LeetCode41.缺失的第一个正数
  • ee trade:黄金投资与股票投资的区别
  • AI视频创作原理
  • idea vue项目删除node_modules时报文件损坏且无法读取,导致删除失败
  • Linux下编译安装-单机模式
  • RSSI定位算法