【[特殊字符]分布式锁通关指南 07】源码剖析redisson利用看门狗机制异步维持客户端锁

1.首先,在获取锁时会触发看门狗机制:


java

代码解读

复制代码

private Long tryAcquire(long leaseTime, TimeUnit unit, long threadId) { Long ttl = null; // 如果未指定租约时间,则使用看门狗机制 if (leaseTime != -1) { ttl = tryLockInnerAsync(leaseTime, unit, threadId, RedisCommands.EVAL_LONG); } else { // 使用默认的看门狗超时时间(默认30秒) ttl = tryLockInnerAsync(commandExecutor.getConnectionManager().getCfg().getLockWatchdogTimeout(), TimeUnit.MILLISECONDS, threadId, RedisCommands.EVAL_LONG); // 启动看门狗 scheduleExpirationRenewal(threadId); } return ttl; }

2.看门狗启动逻辑:


java

代码解读

复制代码

private void scheduleExpirationRenewal(long threadId) { ExpirationEntry entry = new ExpirationEntry(); ExpirationEntry oldEntry = EXPIRATION_RENEWAL_MAP.putIfAbsent(getEntryName(), entry); if (oldEntry != null) { oldEntry.addThreadId(threadId); } else { entry.addThreadId(threadId); // 重点:续期任务的调度 renewExpiration(); } } // 续期任务调度 private void renewExpiration() { // 创建异步续期任务 Timeout task = commandExecutor.getConnectionManager().newTimeout(new TimerTask() { @Override public void run(Timeout timeout) throws Exception { ExpirationEntry ee = EXPIRATION_RENEWAL_MAP.get(getEntryName()); if (ee == null) { return; } // 通过Lua脚本延长锁的过期时间 RFuture<Boolean> future = renewExpirationAsync(threadId); future.onComplete((success, e) -> { if (e != null) { log.error("Can't update lock " + getName() + " expiration", e); return; } if (success) { // 如果续期成功,递归调用,实现循环续期 renewExpiration(); } }); } // 续期间隔为看门狗超时时间的1/3 }, internalLockLeaseTime / 3, TimeUnit.MILLISECONDS); ee.setTimeout(task); }

3.续期的核心 Lua 脚本:


java

代码解读

复制代码

protected RFuture<Boolean> renewExpirationAsync(long threadId) { return commandExecutor.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, // 默认30秒 getLockName(threadId)); }

4.锁释放时清理看门狗:


java

代码解读

复制代码

void cancelExpirationRenewal(Long threadId) { ExpirationEntry task = EXPIRATION_RENEWAL_MAP.get(getEntryName()); if (task == null) { return; } if (threadId != null) { task.removeThreadId(threadId); } if (threadId == null || task.hasNoThreads()) { // 取消定时任务 task.getTimeout().cancel(); EXPIRATION_RENEWAL_MAP.remove(getEntryName()); } }

让我们总结一下看门狗机制的工作流程:

  • 触发条件
    • 当调用 lock() 方法且未指定租约时间时
    • 默认使用 30 秒的看门狗超时时间
  • 核心原理
    • 在获取锁成功后,创建一个定时任务
    • 定时任务每隔 10 秒(默认超时时间的 1/3)执行一次
    • 通过 Lua 脚本检查锁是否还存在并延长过期时间
  • 续期策略
    • 每次续期都将过期时间刷新为 30 秒
    • 只要业务线程还持有锁,就会一直续期
    • 续期操作是异步的,不会阻塞业务线程
  • 自动清理
    • 当锁释放时,自动取消续期任务
    • 通过 EXPIRATION_RENEWAL_MAP 管理所有续期任务
  • 安全保证
    • 使用 Lua 脚本保证续期操作的原子性
    • 只有当前持有锁的线程才能续期成功
    • 如果业务异常导致线程中断,看门狗也会停止续期

另外,通过学习源码,我们要正确使用看门狗机制,我们需要注意:不要使用带超时参数的加锁方法,否则看门狗机制不会生效;确保业务执行时间不会过长,否则会产生大量续期操作;记得在完成业务后及时释放锁,避免资源浪费。

评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值