看门狗啥意思
如果业务代码没执行完,锁却过期了,这时候其他线程又能抢锁了,线程就不安全啦。所以 redisson
内部有个 watchdog
(看门狗)的自动延期的机制,意思是定时监测业务是否执行结束,没结束的话你这个锁是不是快到期了(超过锁的三分之一时间,比如设置的 30s
过期,现在还剩 10s
到期),那就自动续期直到任务结束释放锁,大概是这么个意思。这样做可以防止如果业务代码没执行完,锁却过期了所带来的线程不安全问题。
实际开发中:
1 | // 具有 Watch Dog 自动延期机制 默认续 30s 每隔 30/3=10 秒续到 30s |
- 第 2 种很好理解,最长持有锁就
10s
,超过就释放。 - 第 1 种就会走
watchdog
(看门狗)自动延期这个策略。但是这里是不是就矛盾了,如果业务上有bug
,进到死循环或宕机了,那锁还在不断自动延期,锁不释放那不就造成死锁了?
所以,所以业务上最好是用 tryLock
而不是 lock
。
用到 redisson
最多的场景一定是分布式锁,一个基础的分布式锁具有三个特性:
互斥
:在分布式高并发的条件下,需要保证,同一时刻只能有一个线程获得锁,这是最最基本的一点。防止死锁
:在分布式高并发的条件下,比如有个线程获得锁的同时,还没有来得及去释放锁,就因为系统故障或者其它原因使它无法执行释放锁的命令,导致其它线程都无法获得锁,造成死锁。可重入
(非必要、但重要):我们知道ReentrantLock
是可重入锁,那它的特点就是同一个线程可以重复拿到同一个资源的锁。
实现的方案有很多,这里,就以我们平时在网上常看到的 redis
分布式锁方案为例,来对比看看 redisson
提供的分布式锁有什么高级的地方。
普通的 Redis 分布式锁的缺陷
我们在网上看到的 redis
分布式锁的工具方法,大都满足互斥、防止死锁的特性,有些工具方法会满足可重入特性。
如果只满足上述 3
种特性会有哪些隐患呢? redis
分布式锁无法自动续期,比如,一个锁设置了1
分钟超时释放,如果拿到这个锁的线程在一分钟内没有执行完毕,那么这个锁就会被其他线程拿到,可能会导致严重的线上问题,我已经在秒杀系统故障排查文章中,看到好多因为这个缺陷导致的超卖了。
Redisson 提供的分布式锁
redisson
锁的加锁机制如上图所示,线程去获取锁,获取成功则执行 lua
脚本,保存数据到 redis
数据库。
如果获取失败: 一直通过 while
循环尝试获取锁(可自定义等待时间,超时后返回失败),获取成功后,执行 lua
脚本,保存数据到 redis
数据库。
redisson
提供的分布式锁是支持锁自动续期的,也就是说,如果线程仍旧没有执行完,那么 redisson
会自动给 redis
中的目标 key
延长超时时间,这在 redisson
中称之为 watchdog
机制。
同时 redisson
还有公平锁、读写锁的实现。
watch dog 的自动延期机制
如果拿到分布式锁的节点宕机,且这个锁正好处于锁住的状态时,会出现锁死的状态,为了避免这种情况的发生,锁都会设置一个过期时间。这样也存在一个问题,加入一个线程拿到了锁设置了 30s
超时,在 30s
后这个线程还没有执行完毕,锁超时释放了,就会导致问题, redisson
给出了自己的答案,就是 watchdog
自动延期机制。
redisson
提供了一个监控锁的看门狗,它的作用是在 redisson
实例被关闭前,不断的延长锁的有效期,也就是说,如果一个拿到锁的线程一直没有完成逻辑,那么看门狗会帮助线程不断的延长锁超时时间,锁不会因为超时而被释放。
默认情况下,看门狗的续期时间是 30s
,也可以通过修改 Config.lockWatchdogTimeout
来另行指定。
另外 redisson
还提供了可以指定 leaseTime
参数的加锁方法来指定加锁的时间。超过这个时间后锁便自动解开了,不会延长锁的有效期。
关键结论
上述源码读过来我们可以记住几个关键情报:
watchdog
在当前节点存活时每10s
给分布式锁的key续期30s
;watchdog
机制启动,且代码中没有释放锁操作时,watchdog
会不断的给锁续期;- 从可
2
得出,如果程序释放锁操作时因为异常没有被执行,那么锁无法被释放,所以释放锁操作一定要放到finally {}
中;
可以得出结论:
如果释放锁操作本身异常了, watchdog
还会不停的续期吗?不会,因为无论释放锁操作是否成功, EXPIRATION_RENEWAL_MAP
中的目标 ExpirationEntry
对象已经被移除了, watchdog
通过判断后就不会继续给锁续期了。
以上。