Redisson 分布式锁核心机制解析

Redisson 分布式锁(RLock)的可靠性依赖于其三大核心机制:用于保证原子性的 Lua 脚本、用于自动续期的看门狗(Watchdog)以及用于高效等待的发布/订阅(Pub/Sub)模型。本文旨在解析这三个机制的内部工作原理。

一、Lua 脚本与原子性保证
分布式锁的加锁操作包含多个步骤(如 exists、hset、pexpire),必须作为一个原子单元执行。Redisson 通过将这些操作封装到 Lua 脚本中,利用 Redis 服务端原子性执行脚本的特性,来消除并发竞态条件。

核心 tryLock Lua 脚本逻辑:

— KEYS[1]: 锁名
— ARGV[1]: 锁过期时间
— ARGV[2]: 锁持有者标识 (uuid:threadId)

if (redis.call(‘exists’, KEYS[1]) == 0) then
redis.call(‘hset’, 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]);

二、看门狗(Watchdog)的锁续期机制
为防止因业务执行时间过长导致锁提前过期,Redisson 引入了看门狗机制。

该机制在调用不带 leaseTime 参数的 lock() 方法时被激活。lock() 内部会为 leaseTime 传入 -1,这便是触发看门狗的信号。加锁成功后,若 leaseTime 为 -1,Redisson 会启动一个后台定时任务,负责锁的自动续期。

源码逻辑:

// RedissonLock.java
private <T> RFuture<T> tryAcquireAsync(long waitTime, long leaseTime, TimeUnit unit, long threadId) {
if (leaseTime != -1) { // leaseTime 为 -1 是激活看门狗的关键
return tryLockInnerAsync(waitTime, leaseTime, unit, threadId, RedisCommands.EVAL_LONG);
}
RFuture<Long> ttlFuture = tryLockInnerAsync(waitTime, internalLockLeaseTime, // 使用默认 30s
TimeUnit.MILLISECONDS, threadId, RedisCommands.EVAL_LONG);

ttlFuture.onComplete((ttl, e) -> {
if (e == null) { // 加锁成功后,调度续期任务
scheduleExpirationRenewal(threadId);
}
});
return (RFuture<T>) ttlFuture;
}

private void scheduleExpirationRenewal(long threadId) {
newTimeout(new TimerTask() {
@Override
public void run(Timeout timeout) throws Exception {
RFuture<Boolean> future = renewExpirationAsync(threadId);
future.onComplete((res, e) -> {
if (res) { // 续期成功,则在 10s (默认时间的1/3) 后再次执行
scheduleExpirationRenewal(threadId);
}
});
}
}, internalLockLeaseTime / 3, TimeUnit.MILLISECONDS);
}

默认情况下,锁的过期时间为 30 秒,看门狗会每隔 10 秒检查一次,如果锁仍被当前线程持有,就将其过期时间重置为 30 秒。unlock() 操作会取消该续期任务。

三、发布/订阅(Pub/Sub)的高效等待模型
为避免获取锁失败的线程进行无效的循环轮询(busy-polling),Redisson 采用 Redis 的发布/订阅模型实现高效等待。

当线程尝试获取锁失败时,它会订阅一个与锁名相关的特定 Channel,然后在一个客户端的 Semaphore 上阻塞等待。当锁被释放时(unlock),会向该 Channel 发布一条消息。订阅者收到消息后,释放 Semaphore,从而唤醒等待的线程,使其重新尝试获取锁。

源码逻辑:

// LockPubSub.java – 订阅端
private final Semaphore latch = new Semaphore(0);
private final ChannelFuture subscribeFuture;

// 当收到消息时的回调
@Override
protected void onMessage(String channel, Object msg) {
if (msg.equals(UNLOCK_MESSAGE)) {
latch.release(); // 释放信号量,唤醒等待线程
}
}
public void await() {
latch.acquire(); // 在信号量上阻塞等待
}

// RedissonLock.java – 等待逻辑
private boolean tryLock(long waitTime, … ) {
// … 尝试加锁失败后 …

// 1. 订阅解锁 channel
CompletableFuture<RedissonLockEntry> subscribeFuture = subscribe(getChannelName());

// 2. 在 latch 上等待,waitTime 为超时上限
boolean acquired = entry.getLatch().tryAcquire(waitTime, unit);

// 3. 取消订阅
unsubscribe(subscribeFuture);

// 4. 被唤醒后,再次尝试获取锁
if (acquired) {
return tryAcquireOnce(…);
}
return false;
}

 

欢迎使用66资源网
1. 本站所有资源来源于用户上传和网络,如有侵权请邮件联系站长!
2. 分享目的仅供大家学习和交流,您必须在下载后24小时内删除!
3. 不得使用于非法商业用途,不得违反国家法律。否则后果自负!
4. 本站提供的源码、模板、插件等等其他资源,都不包含技术服务请大家谅解!
5. 如有链接无法下载、失效或广告,请联系管理员处理!
6. 如遇到加密压缩包,请使用WINRAR解压,如遇到无法解压的请联系管理员!
7. 本站有不少源码未能详细测试(解密),不能分辨部分源码是病毒还是误报,所以没有进行任何修改,大家使用前请进行甄别!

66源码网 » Redisson 分布式锁核心机制解析

提供最优质的资源集合

立即查看 了解详情