暂无图片
暂无图片
暂无图片
暂无图片
暂无图片

Redisson分布式可重入锁-加锁超时机制

程序猿西蒙 2021-11-15
2698

NO.1


背景


分布式系统中,针对一些处理失败的问题,经常会使用补偿机制来进行处理,但是补偿机制和业务相关,有时不是很方便抽象出来,此时就会在业务系统中通过定时任务的方式来实现,为了避免单节点问题,业务系统经常部署在多机房的多台机器上,此时定时任务就会同时启动多台,这样会造成资源的浪费,我们希望同时只有一台系统启动,这里会引进分布式锁机制来保障,前面文章将的分布式锁,我们知道如果一个节点获取不到锁,就会无限循环等待,直到获取成功,这不是我们想要的模式,我们希望一个节点尝试在一段时间内获取锁,如果超时我们就自动放弃获取,这样的方式才是我们想要的,那么有什么样的API来实现呢?

NO.2


API分析

阅读Redisson的官网,我们发现可重入锁提供了一种带超时机制的API,保证我们能获取锁并且存在超时的机制,我们先来看一下使用
Config config = new Config();
config.useClusterServers()
.addNodeAddress("redis://192.168.1.15:6379");
RedissonClient client = Redisson.create(config);


RLock lock = client.getLock("anyLock");
lock.tryLock(100, TimeUnit.SECONDS);
lock.unlock();
redisson官网提供了tryLock(time, TimeUnit)的api来实现自带超时机制的加锁,我们看到api使用的很简单,但是有点好奇它是怎么来实现超时机制的呢?

下面我们来看一下,tryLock的底层是怎么来完成超时的控制的


@Override
public boolean tryLock(long waitTime, TimeUnit unit) throws InterruptedException {
return tryLock(waitTime, -1, unit);
}
@Override
public boolean tryLock(long waitTime, long leaseTime, TimeUnit unit) throws InterruptedException {
long time = unit.toMillis(waitTime);
long current = System.currentTimeMillis();
long threadId = Thread.currentThread().getId();
Long ttl = tryAcquire(waitTime, leaseTime, unit, threadId);
// lock acquired
if (ttl == null) {
return true;
    }
time -= System.currentTimeMillis() - current;
if (time <= 0) {
acquireFailed(waitTime, unit, threadId);
return false;
    }
current = System.currentTimeMillis();
RFuture<RedissonLockEntry> subscribeFuture = subscribe(threadId);
if (!subscribeFuture.await(time, TimeUnit.MILLISECONDS)) {
if (!subscribeFuture.cancel(false)) {
subscribeFuture.onComplete((res, e) -> {
if (e == null) {
unsubscribe(subscribeFuture, threadId);
}
});
}
acquireFailed(waitTime, unit, threadId);
return false;
    }
try {
time -= System.currentTimeMillis() - current;
if (time <= 0) {
acquireFailed(waitTime, unit, threadId);
return false;
        }
while (true) {
long currentTime = System.currentTimeMillis();
ttl = tryAcquire(waitTime, leaseTime, unit, threadId);
// lock acquired
if (ttl == null) {
return true;
}


time -= System.currentTimeMillis() - currentTime;
if (time <= 0) {
acquireFailed(waitTime, unit, threadId);
return false;
            }
// waiting for message
currentTime = System.currentTimeMillis();
if (ttl >= 0 && ttl < time) {
subscribeFuture.getNow().getLatch().tryAcquire(ttl, TimeUnit.MILLISECONDS);
} else {
subscribeFuture.getNow().getLatch().tryAcquire(time, TimeUnit.MILLISECONDS);
            }
time -= System.currentTimeMillis() - currentTime;
if (time <= 0) {
acquireFailed(waitTime, unit, threadId);
return false;
}
}
} finally {
unsubscribe(subscribeFuture, threadId);
}
}

分析代码逻辑如下:

  • 先尝试获取分布式锁(这里的逻辑我们前面解析了,这里就不在重复解析),如果获取成功,那么皆大欢喜,就直接返回加锁成功,如果获取锁失败则会执行下面的逻辑

  • 用等待时间减去获取锁的时间,来计算剩余时间

  • 判断剩余时间是否小于0,小于0说明第一次尝试获取锁的时间超过我们规定的时间,那么我们则要放弃获取

  • 剩余时间大于0,则会进入循环,不断的尝试获取锁,并且每次都会计算剩余剩余时间

  • 如果在规定的时间内能成功获取锁,则返回加锁成功,如果在规定的时间内没有获取到锁,那么默认加锁失败



NO.3


总结


带超时机制的加锁逻辑,其实很简单,就是在规定时间内不断的尝试进行加锁,如果加锁成功就返回成功,如果超过规定时间那么就返回获取锁失败


前面文章的链接如下:


redisson分布式可重入锁加锁原理

redisson分布式锁-watch dog原理

分布式可重入锁原理-锁互斥原理

分布式可重入锁原理-释放锁原理


















点个在看你最好看



文章转载自程序猿西蒙,如果涉嫌侵权,请发送邮件至:contact@modb.pro进行举报,并提供相关证据,一经查实,墨天轮将立刻删除相关内容。

评论