文章目录
在分布式锁的实现中, Redisson 的看门狗(Watchdog)机制 是为了解决一个经典难题: "如何给锁设置一个完美的过期时间?"
如果过期时间太短,业务没执行完锁就释放了,会导致并发安全问题;如果过期时间太长(甚至不设过期时间),万一客户端宕机了,锁就会一直留在 Redis 里形成死锁。
一、 核心工作原理
看门狗的核心逻辑可以概括为:自动续期,防止提前失效。
1. 触发条件
并不是所有的 Redisson 锁都会启动看门狗。
- 启动: 只有当你加锁时没有显式指定
leaseTime(或者设为 -1)时,看门狗才会启动。 - 不启动: 如果你写的是
lock.lock(10, TimeUnit.SECONDS),Redisson 会认为你很清楚业务耗时,到期即删,不再续期。
2. 默认参数
- 默认生存时间:
lockWatchdogTimeout默认为 30 秒。 - 续期频率: 每隔
lockWatchdogTimeout / 3的时间(即 10 秒)检查一次。
3. 续期过程
当锁成功获取后,Redisson 会在后台启动一个定时任务(基于 Netty 的 HashedWheelTimer 时间轮):
- 每隔 10 秒,看门狗会检查主线程是否还持有这把锁。
- 如果线程依然存活且未主动释放锁,它会向 Redis 发送一段 Lua 脚本。
- 该脚本负责将该 Key 的过期时间重新重置为 30 秒。
二、 如果客户端宕机了怎么办?
这正是看门狗机制的高明之处:
- 自动释放: 如果持有锁的 Java 进程突然崩溃或断电,看门狗这个后台线程自然也就随之消失了。
- 死锁预防: 此时没有人再给 Redis 里的 Key 续期,那么最多过了 30 秒(默认值),Redis 就会自动删除这个 Key,其他等待的线程就能重新竞争锁。
三、 源码层面的实现逻辑
在 Redisson 的 tryLockInnerAsync 方法中,加锁成功后会调用 scheduleExpirationRenewal:
java
// 伪代码逻辑
private void renewExpiration() {
// 1. 创建一个延时任务
Timeout task = timer.newTimeout(timeout -> {
// 2. 异步执行续期 Lua 脚本
CompletionStage<Boolean> future = renewExpirationAsync(threadId);
future.whenComplete((res, e) -> {
if (res) {
// 3. 如果续期成功,递归调用自身,实现循环续期
renewExpiration();
}
});
}, internalLockLeaseTime / 3, TimeUnit.MILLISECONDS);
}
四、 优缺点分析
优点
- 业务解耦: 开发者不需要预估业务执行时间,避免了因 GC 抖动或网络延迟导致的锁提前失效。
- 安全防死锁: 结合了 Redis 的 TTL 机制和存活检查。
缺点与风险
- 性能开销: 虽然 Lua 脚本很轻量,但大量长耗时任务会导致频繁的续期网络请求。
- 依然存在"异步复制"问题: 看门狗解决了单机 Redis 上的锁过期问题,但如果 Redis 是主从架构,Master 宕机且锁未同步到 Slave,依然可能发生锁丢失(这需要配合 Redlock 算法或使用 ZooKeeper 来解决)。
总结
看门狗就像是一个**"心跳监测器"**。它在不阻塞主业务逻辑的前提下,确保了只要你的程序还在运行,锁就永远不会过期;而一旦你的程序"断气"了,锁也能在短时间内自动归还。
在你的微服务项目中,如果涉及到长时间的任务处理(比如复杂的报表生成或跨系统的 AIGC 内容处理),看门狗是保证分布式锁可靠性的必备工具。
关于 Redisson 的锁,你是否还关注过它的公平锁或者**红锁(Redlock)**的具体应用场景?