1
0
mirror of https://github.com/Snailclimb/JavaGuide synced 2025-08-14 05:21:42 +08:00

[docs update]Redisson 看门狗原理

This commit is contained in:
guide 2022-09-01 15:49:37 +08:00
parent e29154b0e8
commit 1d350536fc
2 changed files with 84 additions and 8 deletions

View File

@ -1,8 +1,6 @@
--- ---
title: 分布式锁 title: 分布式锁
category: 分布式 category: 分布式
--- ---
网上有很多分布式锁相关的文章,写了一个相对简洁易懂的版本,针对面试和工作应该够用了。 网上有很多分布式锁相关的文章,写了一个相对简洁易懂的版本,针对面试和工作应该够用了。
@ -69,6 +67,8 @@ else
end end
``` ```
![Redis 实现简易分布式锁](images/distributed-lock/distributed-lock-setnx.png)
这是一种最简易的 Redis 分布式锁实现,实现方式比较简单,性能也很高效。不过,这种方式实现分布式锁存在一些问题。就比如应用程序遇到一些问题比如释放锁的逻辑突然挂掉,可能会导致锁无法被释放,进而造成共享资源无法再被其他线程/进程访问。 这是一种最简易的 Redis 分布式锁实现,实现方式比较简单,性能也很高效。不过,这种方式实现分布式锁存在一些问题。就比如应用程序遇到一些问题比如释放锁的逻辑突然挂掉,可能会导致锁无法被释放,进而造成共享资源无法再被其他线程/进程访问。
### 为什么要给锁设置一个过期时间? ### 为什么要给锁设置一个过期时间?
@ -91,20 +91,92 @@ OK
你或许在想: **如果操作共享资源的操作还未完成,锁过期时间能够自己续期就好了!** 你或许在想: **如果操作共享资源的操作还未完成,锁过期时间能够自己续期就好了!**
### 如何实现锁的优雅续期?
对于 Java 开发的小伙伴来说,已经有了现成的解决方案:**[Redisson](https://github.com/redisson/redisson)** 。其他语言的解决方案,可以在 Redis 官方文档中找到地址https://redis.io/topics/distlock 。 对于 Java 开发的小伙伴来说,已经有了现成的解决方案:**[Redisson](https://github.com/redisson/redisson)** 。其他语言的解决方案,可以在 Redis 官方文档中找到地址https://redis.io/topics/distlock 。
![Distributed locks with Redis](https://guide-blog-images.oss-cn-shenzhen.aliyuncs.com/github/javaguide/redis-distributed-lock.png) ![Distributed locks with Redis](https://guide-blog-images.oss-cn-shenzhen.aliyuncs.com/github/javaguide/redis-distributed-lock.png)
Redisson 是一个开源的 Java 语言 Redis 客户端,提供了很多开箱即用的功能,不仅仅包括多种分布式锁的实现。 Redisson 是一个开源的 Java 语言 Redis 客户端,提供了很多开箱即用的功能,不仅仅包括多种分布式锁的实现。并且Redisson 还支持 Redis 单机、Redis Sentinel 、Redis Cluster 等多种部署架构。
Redisson 中的分布式锁自带自动续期机制,它提供了一个专门用来监控锁的 **Watch Dog 看门狗)**如果操作共享资源的还未完成的话Watch Dog 会不断地延长锁的过期时间,进而保证锁不会因为超时而被释放。 Redisson 中的分布式锁自带自动续期机制,使用起来非常简单,原理也比较简单,其提供了一个专门用来监控和续期锁的 **Watch Dog 看门狗)**如果操作共享资源的线程还未执行完成的话Watch Dog 会不断地延长锁的过期时间,进而保证锁不会因为超时而被释放。
看门狗名字的由来于 `getLockWatchdogTimeou()` 方法,这个方法返回的是看门狗给锁续期的过期时间,默认为 30 秒([redisson-3.17.6](https://github.com/redisson/redisson/releases/tag/redisson-3.17.6))。
```java
//默认 30秒支持修改
private long lockWatchdogTimeout = 30 * 1000;
public Config setLockWatchdogTimeout(long lockWatchdogTimeout) {
this.lockWatchdogTimeout = lockWatchdogTimeout;
return this;
}
public long getLockWatchdogTimeout() {
return lockWatchdogTimeout;
}
```
`renewExpiration()` 方法包含了看门狗的主要逻辑:
```java
private void renewExpiration() {
//......
Timeout task = commandExecutor.getConnectionManager().newTimeout(new TimerTask() {
@Override
public void run(Timeout timeout) throws Exception {
//......
// 异步续期,基于 Lua 脚本
CompletionStage<Boolean> future = renewExpirationAsync(threadId);
future.whenComplete((res, e) -> {
if (e != null) {
// 无法续期
log.error("Can't update lock " + getRawName() + " expiration", e);
EXPIRATION_RENEWAL_MAP.remove(getEntryName());
return;
}
if (res) {
// 递归调用实现续期
renewExpiration();
} else {
// 取消续期
cancelExpirationRenewal(null);
}
});
}
// 延迟 internalLockLeaseTime/3默认 10s也就是 30/3 再调用
}, internalLockLeaseTime / 3, TimeUnit.MILLISECONDS);
ee.setTimeout(task);
}
```
默认情况下,每过 10 秒,看门狗就会执行续期操作,将锁的超时时间设置为 30 秒。看门狗续期前也会先判断是否需要执行续期操作,需要才会执行续期,否则取消续期操作。
Watch Dog 通过调用 `renewExpirationAsync()` 方法实现锁的异步续期:
```java
protected CompletionStage<Boolean> renewExpirationAsync(long threadId) {
return evalWriteAsync(getRawName(), LongCodec.INSTANCE, RedisCommands.EVAL_BOOLEAN,
// 如果指定的锁存在就续期,将其过期时间设置为 30s默认
"if (redis.call('hexists', KEYS[1], ARGV[2]) == 1) then " +
"redis.call('pexpire', KEYS[1], ARGV[1]); " +
"return 1; " +
"end; " +
"return 0;",
Collections.singletonList(getRawName()),
internalLockLeaseTime, getLockName(threadId));
}
```
可以看出, `renewExpirationAsync` 方法其实是调用 Lua 脚本实现的续期,这样做主要是为了保证续期操作的原子性。
我这里以 Redisson 的分布式可重入锁 `RLock` 为例来说明如何使用 Redisson 实现分布式锁: 我这里以 Redisson 的分布式可重入锁 `RLock` 为例来说明如何使用 Redisson 实现分布式锁:
```java ```java
// 1.获取指定的分布式锁对象 // 1.获取指定的分布式锁对象
RLock lock = redisson.getLock("lock"); RLock lock = redisson.getLock("lock");
// 2.拿锁,具有 Watch Dog 自动续期机制 // 2.拿锁且不设置锁超时时间,具备 Watch Dog 自动续期机制
lock.lock(); lock.lock();
// 3.执行业务 // 3.执行业务
... ...
@ -112,7 +184,12 @@ lock.lock();
lock.unlock(); lock.unlock();
``` ```
可以看出,代码非常简洁直观。 只有未指定锁超时时间,才会使用到 Watch Dog 自动续期机制。
```java
// 手动给锁设置过期时间,不具备 Watch Dog 自动续期机制
lock.lock(10, TimeUnit.SECONDS);
```
如果使用 Redis 来实现分布式锁的话,还是比较推荐直接基于 Redisson 来做的。 如果使用 Redis 来实现分布式锁的话,还是比较推荐直接基于 Redisson 来做的。
@ -135,4 +212,3 @@ Redlock 实现比较复杂,性能比较差,发生时钟变迁的情况下还
实际项目中不建议使用 Redlock 算法,成本和收益不成正比。 实际项目中不建议使用 Redlock 算法,成本和收益不成正比。
如果不是非要实现绝对可靠的分布式锁的话,其实单机版 Redis 就完全够了,实现简单,性能也非常高。如果你必须要实现一个绝对可靠的分布式锁的话,可以基于 Zookeeper 来做,只是性能会差一些。 如果不是非要实现绝对可靠的分布式锁的话,其实单机版 Redis 就完全够了,实现简单,性能也非常高。如果你必须要实现一个绝对可靠的分布式锁的话,可以基于 Zookeeper 来做,只是性能会差一些。

Binary file not shown.

After

Width:  |  Height:  |  Size: 21 KiB