快速解决Watchdog 机制在解锁失败
Redisson 的 Watchdog 机制在解锁失败时不会一直续期锁,它的这个行为取决于解锁操作的触发状态和客户端运行状态。
一、解锁失败时 Watchdog 的续期逻辑
-
正常的解锁流程(
unlock()
被调用)-
无论解锁是否成功(如 Redis 命令执行失败),Redisson 都会在
unlock()
方法中主动停止 Watchdog 的续期任务。 -
源码逻辑:
-
执行解锁 Lua 脚本后,调用
cancelExpirationRenewal(threadId)
方法,从本地缓存EXPIRATION_RENEWAL_MAP
中移除续期任务条目。 -
移除后,Watchdog 线程在下次续期检查时发现缓存条目不存在,立即终止续期。
-
-
结论:只要调用了
unlock()
,无论 Redis 操作结果如何,续期都会停止。
-
-
未调用的解锁(比如哈代码遗漏或异常)
-
如果业务未执行
unlock()
(如异常未进入finally
块),Watchdog 会持续续期,直到:-
进程崩溃:Watchdog 线程终止,锁在
lockWatchdogTimeout
(默认 30 秒)后自动过期。 -
显式停止:通过重启服务或手动删除 Redis 中的锁 Key。
-
-
二、极端场景分析
-
解锁命令发送失败(如网络故障)
-
如果哈
unlock()
中的 Lua 脚本因网络问题未到达 Redis,但本地已清除续期任务,Watchdog 仍会停止续期。此时锁会在 Redis 中自然过期(默认 30 秒)。 -
风险:锁未立即释放,但不会永久持有。
-
-
线程崩溃但 JVM 存活
-
Watchdog 通过
Thread.isAlive()
检查持有锁的线程状态。若线程崩溃(但 JVM 未退出),Watchdog 检测到线程不存活后停止续期,锁超时释放。
-
-
JVM 崩溃或宕机
-
Watchdog 线程随进程终止,续期完全停止,锁在 Redis 中超时后自动删除。
-
三、规避续期问题的实践建议
-
确保解锁执行
-
释放锁的操作必须置于
finally
块,避免异常导致未调用unlock()
:RLock lock = redisson.getLock("lock"); lock.lock(); try { // 业务逻辑 } finally { lock.unlock(); // 确保执行 }
-
-
避免显式设置
leaseTime
-
若使用
lock(10, TimeUnit.SECONDS)
指定租期,Watchdog 会被禁用,锁到期自动释放,无需担心续期问题(但需评估业务执行时间)。
-
-
合理配置超时时间
-
设置
lockWatchdogTimeout
(默认 30 秒)时需大于业务最大执行时间,并预留网络延迟缓冲(建议 ≥ 60 秒)。
-
总结一下下
场景 |
Watchdog 行为 |
锁释放结果 |
---|---|---|
调用 |
立即停止续期 |
锁按预期释放或超时删除 |
未调用 |
持续续期 |
需人工干预或等待进程崩溃 |
进程崩溃 |
续期终止 |
锁超时(默认 30 秒)后释放 |
网络故障导致解锁失败 |
续期停止,锁在 Redis 中超时释放 |
最终一致性保障 |
-
始终在
finally
中调用unlock()
,并监控锁释放日志。 -
非确定业务执行时间的场景,使用无参
lock()
启用 Watchdog,避免手动设置leaseTime
。
- 点赞
- 收藏
- 关注作者
评论(0)