Redisson 分布式锁“看门狗”自动续期源码解剖
3)看门狗启动核心:scheduleExpirationRenewal(long threadId)`scheduleExpirationRenewal(threadId)` 注册状态。`renewExpiration()` 创建 Netty TimerTask。4)周期续期核心:renewExpiration()`renewExpiration()`(递归下一轮)加锁入口 调用 `lock()`
·
Redisson 分布式锁“看门狗”自动续期源码解剖
一、概述
- 目标:围绕 Redisson 自动续期(Watchdog),进行“核心类+核心方法+行级解析”重构版技术博客,帮助从“懂原理”进阶到“懂源码+懂设计”。
- 主线:两个核心类+三大关键方法,串起“加锁→启动看门狗→周期续期→取消续期”的完整闭环。
- 方法:源码行级解析+时序图+架构图+设计模式+工程实践+面试速记口。
二、简介与项目背景
- 痛点:固定 TTL 下,业务线程不可预估阻塞或崩溃,可能出现“锁尚在用却过期”的并发隐患。
- 方案:Redisson 看门狗自动续期,持锁线程存活期间定期延长 TTL,避免误过期。
- 触发条件:未指定租约(leaseTime=-1)时,自动启动看门狗。
三、核心类
- 分布式锁主类:
RedissonLock - 续期状态载体:
ExpirationEntry(含Timeout引用)
四、核心代码:行级解析(2 类 3 法)
1)入口触发:lock()
- 行级解析:
lockWatchdogTimeout:默认看门狗超时时间 30000ms。lock():无参加锁,调用lock(-1, null, false),约定 leaseTime=-1 表示启用看门狗。lock(long leaseTime, TimeUnit unit, boolean interruptibly):计算threadId = Thread.currentThread().getId(),调用tryAcquire(-1, leaseTime, unit, threadId)。tryAcquire返回 ttl==null:加锁成功,进入后续逻辑。
2)看门狗启动入口:tryAcquireAsync(...)
- 行级解析:
tryLockInnerAsync(..., RedisCommands.EVAL_LONG):发起 Lua 加锁,返回 TTL 或 null。ttlRemainingFuture.onComplete((ttlRemaining, e) -> {...}):e != null:异常则返回。ttlRemaining == null:加锁成功分支:
3)看门狗启动核心:scheduleExpirationRenewal(long threadId)
- 行级解析:
4)周期续期核心:renewExpiration()
- 行级解析:
ExpirationEntry ee = EXPIRATION_RENEWAL_MAP.get(getEntryName()):状态不存在直接返回。newTimeout(new TimerTask(){...}, lockWatchdogTimeout/3, TimeUnit.MILLISECONDS):以 Netty 定时器创建任务(默认 10s)。TimerTask.run(...)内:ExpirationEntry ent = EXPIRATION_RENEWAL_MAP.get(getEntryName())。Long threadId = ent.getFirstThreadId()。RFuture(Boolean) future = renewExpirationAsync(threadId)。future.whenComplete((res, e) -> {...}):e != null:记录错误并停止本轮。res == true:递归调用renewExpiration()。else cancelExpirationRenewal(threadId):取消看门狗并清理。
ee.setTimeout(task):记录定时任务句柄。
5)异步续期原子实现:renewExpirationAsync(long threadId)
- 行级解析:
evalWriteAsync(..., RedisCommands.EVAL_BOOLEAN, "lua...", keys, internalLockLeaseTime, getLockName(threadId)):- Lua:
hexists KEYS[1], ARGV[2]:当前线程仍持有锁。pexpire KEYS[1], ARGV[1]:TTL 延至internalLockLeaseTime(默认 30s)。- 返回 1 表示续期成功,0 表示失败。
- Lua:
6)续期状态对象:ExpirationEntry
- 行级解析:
Map(Long, Integer) threadIds:线程ID→重入计数。volatile Timeout timeout:当前定时任务句柄。addThreadId(long)/removeThreadId(long):重入管理。hasNoThreads()、getFirstThreadId():校验与选取。
五、可视化(颜色与样式优化)
1)主流程图(Mermaid)
2)时序图(Mermaid)
六、设计模式点睛
- 模板方法/钩子:
lock()→ 异步完成钩子按leaseTime决定是否scheduleExpirationRenewal()。 - 命令模式:续期 Lua 封装为
RedisCommands.EVAL_BOOLEAN,经evalWriteAsync(...)路由。 - 观察者/回调:
onComplete、whenComplete驱动续期与取消。 - 享元/缓存:
EXPIRATION_RENEWAL_MAP共享续期状态。 - 适配器:续期逻辑适配
TimerTask/Timeout。 - 装饰器(语义扩展):
RedissonLock继承RedissonExpirable。
七、工程实践与边界
- 节奏:watchdog=30s,续期周期=10s(1/3);可调
Config.setLockWatchdogTimeout(...)。 - 场景选择:不确定耗时/可能阻塞→看门狗;确定性短事务→固定
leaseTime。 - 故障语义:线程崩溃或连接异常→续期停止,锁在窗口后自动过期。
- 观测性:采集续期成功/失败/取消次数与回调异常;压测 EventLoop 负载与时间轮稳定性。
八、面试速记口
- 口诀一:
lock(-1)启动看门狗;tryAcquire成功→schedule;renewExpiration定时 Luapexpire递归。 - 口诀二:时间轮 O(1);回调驱动;Map 共享;Lua 原子。
- 口诀三:模板钩子+命令封装+观察回调+享元缓存+适配器联动 Netty。
- 口诀四:成功递归、失败取消;线程崩溃自动过期;
leaseTime>0禁看门狗。
九、权威资料与参考
- Redisson GitHub:https://github.com/redisson/redisson
- Redisson Locks 文档:https://github.com/redisson/redisson/wiki/8.-distributed-locks-and-synchronizers
- Netty 官方与时间轮:https://netty.io/
- Redis EVAL/Lua 文档:https://redis.io/commands/eval/
- 类索引:
十、总结
- 看门狗以“Netty 时间轮+Lua 原子续期+状态表共享”保障持锁期间不误过期。
- 两类三法串起核心路径,设计模式提升稳定性与可扩展性。
- 实战策略:不确定耗时用看门狗;确定性短事务用固定租约;完善告警与压测。
更多推荐


所有评论(0)