【Redisson】分布式锁源码分析如何实现多个应用实例互斥

入口org.redisson.api.RedissonClient

@Resource
private RedissonClient redissonClient;
...

RLock rLock = redissonClient.getLock(lockName);

lockName就是保存到Redis里面的key

进入org.redisson.Redisson.getLock

    @Override
    public RLock getLock(String name) {
        return new RedissonLock(commandExecutor, name);
    }

进入org.redisson.RedissonLock

直接进行构建方法里面的super(commandExecutor, name);

    public RedissonBaseLock(CommandAsyncExecutor commandExecutor, String name) {
        super(commandExecutor, name);
        this.id = getServiceManager().getId();
        this.internalLockLeaseTime = getServiceManager().getCfg().getLockWatchdogTimeout();
        this.entryName = id + ":" + name;
    }
  • org.redisson.connection.ServiceManager: private final String id = UUID.randomUUID().toString();
  • 这个id就是UUID: this.id = getServiceManager().getId();
  • 这个entryName通过UUID可以区分是哪个应用实例
  • entryName+threadId可以区分哪个应用实例的哪个进程持有锁

rLock.tryLock(60, TimeUnit.SECONDS);

尝试获取锁

进入org.redisson.RedissonLock

    <T> RFuture<T> tryLockInnerAsync(long waitTime, long leaseTime, TimeUnit unit, long threadId, RedisStrictCommand<T> command) {
        return commandExecutor.syncedEval(getRawName(), LongCodec.INSTANCE, command,
                "if ((redis.call('exists', KEYS[1]) == 0) " +
                            "or (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(getRawName()), unit.toMillis(leaseTime), getLockName(threadId));
    }
  • Collections.singletonList(getRawName()),这个参数就是Redis的key
  • unit.toMillis(leaseTime),这个参数是Lua脚本的第1个参数,即ARGV[1]
  • getLockName(threadId),这个参数是Lua脚本的第2个参数,即ARGV[2]
  • 这段Lua脚本的含义解析
  • (redis.call('exists', KEYS[1]) == 0),锁不存在
  • or (redis.call('hexists', KEYS[1], ARGV[2]) == 1)),锁存在,但是当前实例线程拥有该锁
  • 以上两种情况,均代表加锁成功,则返回null
  • 否则返回这个锁对应的TTL时间
  • 所以外层调用的地方是根据返回的ttl是否为null来判断加锁是否成功
        Long ttl = tryAcquire(waitTime, leaseTime, unit, threadId);
        // lock acquired
        if (ttl == null) {
            return true;
        }

进入Lua脚本的第2个参数getLockName

    protected String getLockName(long threadId) {
        return id + ":" + threadId;
    }

这个id就是上面提到的UUID,结合线程ID,可以判断是哪个应用实例的哪个进程持有锁

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

太空眼睛

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值