Redisson分布式锁,重试锁和锁续命的原理

RedissonLock 锁重试原理

tryLock有三个三个参数,第一个是等待时间,第二个是锁失效后自动释放的时间,不填默认为-1,第三个是时间单位;

当设置了第一个参数,那这个锁就成了可重试锁;获取锁失败后,就不会立即返回了;会在等待内不断重试;如果在等待时间结束后,还没有获取到锁,那就失败了。

获取当前时间,线程ID,尝试获取锁,判断锁失效后自动释放的时间是否等于-1,如果不等于,就用自己的锁释放时间,如果等于-1,异步调用tryLockInnerAsync,返回值是个Future,第一个参数是等待时间,第二个参数是锁释放时间,看门狗的默认30秒,第三个是时间单位,第四个参数是线程ID,这个方法内是个lua脚本,成功返回空,是否返回过期时间;判断返回值是否为空,不为空,计算剩余等待时间,判断等待时间>0,大于就去尝试获取锁,但不是立即获取锁,是在剩余等待时间内订阅了锁释放的情况(锁释放的时候会发布通知),返回值也是个Future,如果超时了,会取消订阅;如果锁已经释放了,计算剩余等待时间,判断剩余等待时间>0,开始循环,就可以重新获取锁,和上面一样,如果失败,同时上,但这里用的是信号量。


lock.tryLock方法

复制代码
第一个参数(等待时间),如果设置了,获取锁失败后,就不会立即返回了;会在等待内不断重试;如果在等待时间结束后,还没有获取到锁,那就失败了。所以设置后就变成了可重试的锁了。
第二个参数(锁失效后自动释放的时间,不填默认为-1)
第三个参数(时间单位)
源码跟进

第二个参数没给,默认值-1,跟进去tryLock方法看看


跟进tryAcquire方法,尝试获取锁

跟进tryAcquireAsync方法

java 复制代码
//源码
  private <T> RFuture<Long> tryAcquireAsync(long waitTime, long leaseTime, TimeUnit unit, long threadId) {	
 		 //判断释放锁的时间是否为-1
        if (leaseTime != -1L) {
       		 //用自己的释放锁的时间
            return this.tryLockInnerAsync(waitTime, leaseTime, unit, threadId, RedisCommands.EVAL_LONG);
        } else {
        	//如果是-1,设置一个默认的释放锁的时间,30秒(getLockWatchdogTimeout)
            RFuture<Long> ttlRemainingFuture = this.tryLockInnerAsync(waitTime, this.commandExecutor.getConnectionManager().getCfg().getLockWatchdogTimeout(), TimeUnit.MILLISECONDS, threadId, RedisCommands.EVAL_LONG);
            ttlRemainingFuture.onComplete((ttlRemaining, e) -> {
                if (e == null) {
                    if (ttlRemaining == null) {
                        this.scheduleExpirationRenewal(threadId);
                    }

                }
            });
            return ttlRemainingFuture;
        }
    }


跟进getLockWatchdogTimeout看门狗

显示默认30秒

返回RedissonLock类,继续跟进tryLockInnerAsync方法

tryLockInnerAsync 异步方法,有没有拿到结果不清楚

看到有lua脚本

lua脚本

判断锁是否存在

不存在设置锁标识,v+1

设置有效期

存在,判断锁是不是我自己的

是,v+1

设置有效期

成功获取锁,返回空

失败,返回锁的有效期(毫秒)

java 复制代码
  <T> RFuture<T> tryLockInnerAsync(long waitTime, long leaseTime, TimeUnit unit, long threadId, RedisStrictCommand<T> command) {
  		//释放锁的时间,记录到本地一个成员变量里
        this.internalLockLeaseTime = unit.toMillis(leaseTime);
        return this.evalWriteAsync(this.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(this.getName()), this.internalLockLeaseTime, this.getLockName(threadId));
    }

一步一步往上返回,返回到RedissonLock类中的tryLock方法的tryAcquire方法,结果有两种,一种是获取锁成功返回值为空,另一种是获取锁失败返回值为锁释放的时间


继续

锁续命

当用默认的锁释放时间时,且已经拿到锁,会创建一个 ConcurrentMap来存锁的记录,以锁的名称为k,线程id为V,判断是否第一次来,如果是开启一个延时任务,10秒后执行,执行的就是更新有效期的lua脚本,更新成功后,开始递归,无限续约。

先看锁释放时间是默认值-1的情况

跟进 scheduleExpirationRenewal 方法

跟进 renewExpiration 方法

跟进 renewExpirationAsync 方法

判断锁是不是当前线程拿到的,是就重置有效期

java 复制代码
   protected RFuture<Boolean> renewExpirationAsync(long threadId) {
        return this.evalWriteAsync(this.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(this.getName()), this.internalLockLeaseTime, this.getLockName(threadId));
    }

执行lua脚本后

释放锁


相关推荐
Jay Kay2 小时前
TensorFlow内核剖析:分布式TensorFlow架构解析与实战指南
分布式·架构·tensorflow
亿牛云爬虫专家4 小时前
Kubernetes下的分布式采集系统设计与实战:趋势监测失效引发的架构进化
分布式·python·架构·kubernetes·爬虫代理·监测·采集
群联云防护小杜10 小时前
构建分布式高防架构实现业务零中断
前端·网络·分布式·tcp/ip·安全·游戏·架构
爱吃面的猫10 小时前
大数据Hadoop之——Flink1.17.0安装与使用(非常详细)
大数据·hadoop·分布式
上上迁12 小时前
分布式生成 ID 策略的演进和最佳实践,含springBoot 实现(Java版本)
java·spring boot·分布式
长路 ㅤ   12 小时前
Java后端技术博客汇总文档
分布式·算法·技术分享·编程学习·java后端
暗影八度14 小时前
Spark流水线数据质量检查组件
大数据·分布式·spark
CodeWithMe15 小时前
【Note】《Kafka: The Definitive Guide》 第5章:深入 Kafka 内部结构,理解分布式日志系统的核心奥秘
分布式·kafka
CodeWithMe15 小时前
【Note】《Kafka: The Definitive Guide》第一章:Meet Kafka
分布式·kafka
CodeWithMe15 小时前
【Note】《Kafka: The Definitive Guide》 第二章 Installing Kafka:Kafka 安装与运行
分布式·kafka