首页
学习
活动
专区
圈层
工具
发布
社区首页 >专栏 >压测结束CPU依旧100%不降!深藏Redisson底层隐秘死循环Bug,90%团队都踩坑且无法感知

压测结束CPU依旧100%不降!深藏Redisson底层隐秘死循环Bug,90%团队都踩坑且无法感知

作者头像
用户12591013
发布2026-07-10 15:31:30
发布2026-07-10 15:31:30
70
举报

前言

线上诡异疑难Bug:接口压测早已停止,流量归零,但是应用CPU永久打满100%,机器持续告警,只能重启临时恢复。

无业务死循环、无慢SQL、无频繁GC、无热点Key,业务代码零问题,排查全程毫无头绪。

最终定位:不是业务代码问题,是Redisson客户端底层隐藏极端边界死循环Bug

该问题概率极低,日常运行完全无感,只会在Redis网络异常、命令写入失败、连接归还重试链路触发;官方虽已修复,但需要大版本升级。绝大多数企业受限于Spring版本、中间件依赖,根本不敢升级Redisson。

本文手把手完整复盘:线程栈排查、源码逐行剖析死循环成因、官方PR修复思路、不升级依赖,本地源码热修复落地方案,看完彻底搞定该隐秘线上故障。

一、故障现场:极其诡异的线上现象

1. 故障表现

全链路压测完成,线上流量直接归零,无任何业务请求进入服务

应用CPU持续100%居高不下,不会自动回落,机器负载持续飙升

服务接口响应超时、心跳检测异常,业务近乎不可用

重启应用后立刻恢复正常,一段时间后大概率复现,无固定复现规律

2. 常规排查全部无效

起初依次排查常规CPU高占用诱因,全部排除:

✅ 无业务自定义死循环代码

✅ GC日志正常,无频繁FullGC、无内存泄漏

✅ 数据库无慢查询、无连接池打满

✅ Redis服务端指标完全正常,无CPU、内存、连接异常

所有业务层面监控全部正常,故障矛头直指第三方中间件客户端:Redisson

二、标准线上排查流程(可直接复用)

步骤1:定位占用CPU最高的线程

执行命令,定位进程内高CPU线程:

代码语言:javascript
复制
#查看进程下所有线程CPU占用
top -H -p 应用pid

观测结果:线程号189持续霸占CPU,占用率拉满。

步骤2:线程ID转16进制,匹配dump堆栈

Java线程dump堆栈中线程ID为16进制,转换:189 = 0xBD

导出线程栈

代码语言:javascript
复制
#导出线程栈信息, pid是java进程id
jstack pid

线程dump文件内容(找到nid=0xbd的内容)

代码语言:javascript
复制
"redisson-netty-2-6" #184 prio=5 os_prio=0 cpu=295856.81ms elapsed=3109.81s nid=0xbd runnable
java.lang.Thread.State: RUNNABLE
at org.redisson.client.handler.CommandsQueue.write(CommandsQueue.java:79)
// 后续一长串Netty、Redisson连接归还、命令写入、异常回调链路堆栈
...

关键结论

卡死位置固定:Redisson CommandsQueue 第79行,Netty工作线程一直处于RUNNABLE运行态,无限空转死循环,永久霸占CPU。

三、源码逐行分析:为什么会无限死循环?

1. 问题源码(旧版Redisson原生代码)

CommandsQueue.write 核心逻辑极简,采用AtomicBoolean做自旋锁控制命令串行写入:

代码语言:javascript
复制
// 原子标记锁
private final AtomicBoolean lock = new AtomicBoolean();
public void write(...) {    
// 第79行:死循环自旋抢占锁    
while (!lock.compareAndSet(false, true)) {        
  
 try {        
 // 执行Redis命令写入Netty通道        
 doWriteCommand();    
 } 
 finally {        
 // 第89行:释放锁,重置标记        
 lock.set(false);    
 }
}

2. 绝大多数人的误区

第一眼看上去代码完全没问题:CAS自旋抢锁,finally保证一定释放锁,标准无锁自旋写法。

但Redisson复杂异步回调链路下,出现了极端异常时序

3. 既然定位到redisson的问题,本人向redisson作者提交了issue,

完整复现故障堆栈、描述问题场景,官方维护者直接给出精准定位与修复方案。

issue地址:https://github.com/redisson/redisson/issues/6153

下方为官方作者截图内容【原版回复原文】(一字未改,和后台截图完全一致)

mrniko(Redisson官方维护者)原话:The deadlock is that the CommandsQueue#write are invoked twice in one stack. Obviously, it cannot obtain the lock at the second time.

精准中文翻译:本次死循环问题根源为,同一个调用栈中,CommandsQueue#write方法被连续调用两次。线程第二次尝试获取锁时,永远获取不到锁,从而陷入无限自旋。

4.真实根因(对应官方GitHub PR#6203)

结合官方开发者复盘 + 完整调用链路堆栈,还原唯一触发时序:

  • 同一根Netty线程,同一个调用栈内,二次重复执行CommandsQueue.write方法
  • 第一次write:CAS抢占锁成功,lock=true,开始执行业务写入逻辑
  • 写入过程触发网络异常,进入异常回调、连接归还重试链路
  • 未执行finally、未执行lock.set(false)释放锁,代码再次嵌套进入write方法
  • 第二次进来:lock一直是true,CAS永远失败,while无限循环 + Thread.yield无效
  • 最终结果:Netty工作线程永久死循环,CPU直接打满100%,永不退出

一句话通俗总结单线程可重入调用write,原生CAS非可重入锁不支持重入,锁无法释放,直接卡死死循环。

四、官方修复方案 & 企业落地痛点

1. 官方标准修复(PR#6203)

官方修复思路极其直白,直击痛点:

把不可重入的AtomicBoolean自旋锁,替换为JDK原生ReentrantLock可重入锁

允许同一线程重复抢占锁,彻底解决单线程重入导致的永久自旋死循环。

2. 企业无法升级的致命痛点

线上生产环境无法直接升级高版本Redisson,原因:

新版Redisson依赖更高版本Spring、SpringBoot

工程整体Spring体系无法大范围升级,改动风险极高

回归测试工作量巨大,生产环境禁止贸然升级中间件大版本

不能升级版本,但是线上CPU100%故障必须解决,只能手动本地源码修复


五、零依赖升级:本地源码热修复方案(可直接上线)

无需改动项目任何业务代码、无需升级任何依赖,直接覆盖Redisson原生类即可修复。

修复思路

完全对齐官方PR#6203修复逻辑,自行替换锁实现:

删除原生 AtomicBoolean 自旋锁

替换为 ReentrantLock 可重入锁

保证同一线程重入write方法可以正常获取锁,杜绝无限自旋

修复前后代码对比

❌ 旧版问题代码

✅ 本地修复后代码(对齐官方PR)

https://github.com/redisson/redisson/pull/6203

修复效果

  • 同一线程嵌套二次调用write:可重入锁直接放行,无自旋等待
  • 多线程并发写入:依旧保持串行互斥,不破坏原有命令排队写入能力
  • 彻底消灭while空转死循环,压测后CPU平稳回落,故障彻底根治

六、复盘总结 & 线上避坑清单

1. Bug核心复盘

这不是常规业务Bug,是Redisson异步回调+Netty线程模型+非可重入自旋锁叠加的极端边界Bug。日常开发、常规压测完全无法复现,只有网络异常、命令写入失败、连接归还重试链路才会触发,隐蔽性极强。

2. 开发避坑要点

自己手写自旋锁一定要考虑线程重入场景,无脑AtomicBoolean自旋锁存在巨大隐患

Netty EventLoop是单线程模型,嵌套回调极易触发方法重入,锁选型必须谨慎

生产中间件不要盲目追新版本,但是要提前熟知已知高危Bug,提前做本地补丁

CPU莫名100%无业务死循环,优先排查Netty线程、中间件客户端底层死循环

3. 终极建议

如果你们项目使用中等版本Redisson、不敢升级依赖,直接打这个局部源码补丁即可,改动极小、风险为零,彻底规避该隐秘CPU死循环故障。

结束语

很多线上疑难告警,从来都不是业务代码的锅,而是第三方组件深藏多年、极低概率触发的底层Bug。

只会写业务代码、不会看线程堆栈、不会读开源源码、不会做局部源码热修复,遇到这类诡异故障只能盲目重启服务。

读懂中间件底层线程模型、看懂开源官方PR修复思路,才是高级工程师和CRUD业务开发的核心分水岭。

本文参与 腾讯云自媒体同步曝光计划,分享自微信公众号。
原始发表:2026-06-23,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 鹏哥调优笔记 微信公众号,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 前言
  • 一、故障现场:极其诡异的线上现象
    • 1. 故障表现
    • 2. 常规排查全部无效
  • 二、标准线上排查流程(可直接复用)
    • 步骤1:定位占用CPU最高的线程
    • 步骤2:线程ID转16进制,匹配dump堆栈
    • 关键结论
  • 三、源码逐行分析:为什么会无限死循环?
    • 1. 问题源码(旧版Redisson原生代码)
    • 2. 绝大多数人的误区
    • 4.真实根因(对应官方GitHub PR#6203)
  • 四、官方修复方案 & 企业落地痛点
    • 1. 官方标准修复(PR#6203)
    • 2. 企业无法升级的致命痛点
  • 五、零依赖升级:本地源码热修复方案(可直接上线)
    • 修复思路
    • 修复前后代码对比
    • 修复效果
  • 六、复盘总结 & 线上避坑清单
    • 1. Bug核心复盘
    • 2. 开发避坑要点
    • 3. 终极建议
  • 结束语
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档