redisson使用lock导致死锁问题

redisson使用lock导致死锁问题

1、背景

我们是做物联网设备的公司,设备初始化会向设备发送API接口下发agora license,当时当时一个环境的全部agora设备都下发出现了问题,具体表现为:

  1. 当前的线程池告警持续触发拒绝策略(调度线程处理任务),导致运维要求我们进行排查
  2. 线程池资源告警则增加资源,环境中agora微服务已经增加到了6台,但是仍然线程的队列容量稳步上升,增加资源并不能解决问题
  3. 因为任务队列容量较多,导致大量agora设备下发agora都是在初始化的5-6h以后了,涉及设备面广,影响较大

2、排查方式

  1. 使用 jstack 1查看当前的线程池开头的线程状态与堆栈
  2. 发现全部的线程池的线程状态都是WATING状态,查看堆栈,都是在等待Redisson的锁释放
  3. 查看了下对应线程池设计的流程,发现在下发license时确实会tongguolock方法加锁
  4. 查看 腾讯云 日志,发现同一台设备在日志中频繁向设备下发agora license,10s一次,正常流程应当至少2min才初始化下发一次

经过逻辑分析后发现原因:

多个设备出故障频繁注册+redisson.lock() 强制等待锁释放造成线程池资源耗尽

  1. 以设备20252025为例,当下发设备license时,第一次会创建一个20252025的锁,但是这个设备也有其他在线程池的进程进行下发,因为使用的是lock,是无限期等待锁,所以线程池资源来利用不起来,导致触发拒绝策略

    redisson创建分布式锁的lua脚本是

    lua 复制代码
    -- Lua 脚本(原子执行)
    if (redis.call('exists', KEYS[1]) == 0) then
        redis.call('hset', KEYS[1], ARGV[2], 1);
        redis.call('pexpire', KEYS[1], ARGV[1]);
        return nil;
    end;
    if (redis.call('hexists', KEYS[1], ARGV[2]) == 1) then
        redis.call('hincrby', KEYS[1], ARGV[2], 1);
        redis.call('pexpire', KEYS[1], ARGV[1]);
        return nil;
    end;
    return redis.call('pttl', KEYS[1]);

    可以看到value值(Value)的结构: 锁的值存储的是一个字符串(String),内容为锁 持有者的唯一标识符和可重入计数,格式为: :: ·UUID:Redisson客户端实例的唯一标识符(每个客户端启动时生成) ·ThreadID:持有锁的线程ID(支持可重入锁 同一线程多次加锁) ·Count:锁的可重入次数(初始为1,每次重入加1,释放时减1)

    uuid为啥不变 猜测是服务pod没被杀 自己拉起来时候 这个客户端id不变导致的

  2. 在某一次的k8s滚动更新时,可能因为未在对应时间内执行完线程池任务(如最后30s等待关闭时间内,虽然没有新任务进,但是进程1在最后1s释放了锁,进程2在此时拿到了锁),导致finally代码块的解锁逻辑并未执行,导致该锁一直被占用,新pod启动后又给redLock续期导致该锁一直没有被释放

因为这两个原因导致线上出现问题

3、解决方式

  1. 不能使用lock方法而是tryLock,等待一段时间后释放掉锁;并依据业务重要程度业务补救(如本次获取不到锁,很可能是因为当前有正在下发的任务,顾不需要延时任务或者持久化这种补救代码逻辑)且下发逻辑暂时不需要考虑Java代码层面幂等,数据库主键会保证幂等性
  2. 推广一下,其他组件如ReentrantLock也有lock和trylock方法,在开发时也尽量使用tryLock

扩展阅读

  1. Java程序员必备:jstack命令解析
相关推荐
S***26752 小时前
基于SpringBoot和Leaflet的行政区划地图掩膜效果实战
java·spring boot·后端
马剑威(威哥爱编程)2 小时前
鸿蒙6开发视频播放器的屏幕方向适配问题
java·音视频·harmonyos
JIngJaneIL2 小时前
社区互助|社区交易|基于springboot+vue的社区互助交易系统(源码+数据库+文档)
java·数据库·vue.js·spring boot·论文·毕设·社区互助
V***u4533 小时前
MS SQL Server partition by 函数实战二 编排考场人员
java·服务器·开发语言
这是程序猿3 小时前
基于java的ssm框架旅游在线平台
java·开发语言·spring boot·spring·旅游·旅游在线平台
i***t9193 小时前
基于SpringBoot和PostGIS的云南与缅甸的千里边境线实战
java·spring boot·spring
k***08293 小时前
【监控】spring actuator源码速读
java·spring boot·spring
麦麦鸡腿堡4 小时前
Java_网络编程_InetAddress类与Socket类
java·服务器·网络
@大迁世界4 小时前
相信我兄弟:Cloudflare Rust 的 .unwrap() 方法在 330 多个数据中心引发了恐慌
开发语言·后端·rust
vx_dmxq2114 小时前
【PHP考研互助系统】(免费领源码+演示录像)|可做计算机毕设Java、Python、PHP、小程序APP、C#、爬虫大数据、单片机、文案
java·spring boot·mysql·考研·微信小程序·小程序·php