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脚本后

释放锁


相关推荐
爬山算法19 分钟前
Redis(69)Redis分布式锁的优点和缺点是什么?
数据库·redis·分布式
virtuousOne6 小时前
Kafka基础
分布式·kafka
虫小宝6 小时前
Java分布式架构下的电商返利APP技术选型与架构设计实践
java·分布式·架构
007php0076 小时前
百度面试题解析:Zookeeper、ArrayList、生产者消费者模型及多线程(二)
java·分布式·zookeeper·云原生·职场和发展·eureka·java-zookeeper
waving-black6 小时前
windows系统下安装测试kafka
windows·分布式·kafka
深思慎考7 小时前
RabbitMQ 入门:基于 AMQP-CPP 的 C++ 实践指南与二次封装
开发语言·c++·分布式·rabbitmq·api
Andya_net7 小时前
Java | 基于redis实现分布式批量设置各个数据中心的服务器配置方案设计和代码实践
java·服务器·分布式
大数据0020 小时前
CLICKHOUSE分布式表初体验
分布式·clickhouse
失散1320 小时前
分布式专题——43 ElasticSearch概述
java·分布式·elasticsearch·架构
爬山算法1 天前
Redis(66)Redis如何实现分布式锁?
数据库·redis·分布式