Redisson分布式锁
大黄 Lv4
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
<T> RFuture<T> tryLockInnerAsync(long waitTime, long leaseTime, TimeUnit unit, long threadId, RedisStrictCommand<T> command) {
return evalWriteAsync(getName(), LongCodec.INSTANCE, command,
"if (redis.call('exists', KEYS[1]) == 0) then " +
"redis.call('hincrby', KEYS[1], ARGV[2], 1); " +
"redis.call('pexpire', KEYS[1], ARGV[1]); " +
"return nil; " +
"end; " +
"if (redis.call('hexists', KEYS[1], ARGV[2]) == 1) then " +
"redis.call('hincrby', KEYS[1], ARGV[2], 1); " +
"redis.call('pexpire', KEYS[1], ARGV[1]); " +
"return nil; " +
"end; " +
"return redis.call('pttl', KEYS[1]);",
Collections.singletonList(getName()), unit.toMillis(leaseTime), getLockName(threadId));
}

此处的LockName
UUID是基于当前时间、计数器(counter)和硬件标识(通常为无线网卡MAC地址)等数据计算生成的。

1
2
3
4
5
6
7
8
protected String getLockName(long threadId) {
return id + ":" + threadId;
}

this.id = commandExecutor.getConnectionManager().getId();

// debug 发现id是一个 uuid
// 所以不会出现不同服务相同线程id对redis分布式锁上锁成功的情况

加锁流程图
Redisson分布式锁 加锁流程图.png

Redisson Lock的效果
Redisson分布式锁 加锁效果.png

watchdog 锁自动续期(默认每次续30s)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
private void renewExpiration() {
ExpirationEntry ee = EXPIRATION_RENEWAL_MAP.get(getEntryName());
if (ee == null) {
return;
}

Timeout task = commandExecutor.getConnectionManager().newTimeout(new TimerTask() {
@Override
public void run(Timeout timeout) throws Exception {
ExpirationEntry ent = EXPIRATION_RENEWAL_MAP.get(getEntryName());
if (ent == null) {
return;
}
Long threadId = ent.getFirstThreadId();
if (threadId == null) {
return;
}

// 锁续期
RFuture<Boolean> future = renewExpirationAsync(threadId);
future.onComplete((res, e) -> {
// 异常
if (e != null) {
log.error("Can't update lock " + getName() + " expiration", e);
EXPIRATION_RENEWAL_MAP.remove(getEntryName());
return;
}

if (res) {
// reschedule itself
// 续期成功,再提交一个任务
renewExpiration();
}
});
}
}, internalLockLeaseTime / 3, TimeUnit.MILLISECONDS);

ee.setTimeout(task);
}

// 主要判断就是 这个锁是否在 redis 中存在,如果存在就进行 pexpire 延期
protected RFuture<Boolean> renewExpirationAsync(long threadId) {
return evalWriteAsync(getName(), LongCodec.INSTANCE, RedisCommands.EVAL_BOOLEAN,
"if (redis.call('hexists', KEYS[1], ARGV[2]) == 1) then " +
"redis.call('pexpire', KEYS[1], ARGV[1]); " +
"return 1; " +
"end; " +
"return 0;",
Collections.singletonList(getName()),
internalLockLeaseTime, getLockName(threadId));
}

总结

Redisson分布式锁安全的原因:
首先锁的lockName是根据uuid+当前线程id组合生成,不可能存在重复
同时还有一个watch dog自动续期的机制

  • Post title:Redisson分布式锁
  • Post author:大黄
  • Create time:2022-08-19 14:05:43
  • Post link:https://huangbangjing.cn/2022/08/19/Redisson分布式锁/
  • Copyright Notice:All articles in this blog are licensed under BY-NC-SA unless stating additionally.