锁续期
- 当代码还没有执行完,但是redis设置的时间到了,导致redis锁失效了,出现并发问题,因此我们需要锁续期。
实现原理
实时检查上锁的资源是否已经结束,如果没有结束的话,检查时间是否超过三分之一,如果超过三分之一,就执行锁续期,延长时间
redis加锁代码
@Override
public void lock() {
try {
lock(-1, null, false);
} catch (InterruptedException e) {
throw new IllegalStateException();
}
}
private void lock(long leaseTime, TimeUnit unit, boolean interruptibly) throws InterruptedException {
long threadId = Thread.currentThread().getId();
// 加锁方法
Long ttl = tryAcquire(-1, leaseTime, unit, threadId);
// 加锁成功
if (ttl == null) {
return;
}
// 加锁失败,while(true)等待重试。
}
WatchDog代码
// leaseTime 是从上面方法中传入过来的 只有 lock才有会WatchDog逻辑
private <T> RFuture<Long> tryAcquireAsync(long waitTime, long leaseTime, TimeUnit unit, long threadId) {
RFuture<Boolean> ttlRemainingFuture;
ttlRemainingFuture.onComplete((ttlRemaining, e) -> {
// ttlRemaining == null 代表加锁成功。
if (ttlRemaining == null) {
// leaseTime == -1就scheduleExpirationRenewal开启看门狗续期,
// leaseTime != -1就不续期,只是把internalLockLeaseTime时间变成传进来的时间。
if (leaseTime != -1) {
internalLockLeaseTime = unit.toMillis(leaseTime);
} else {
scheduleExpirationRenewal(threadId);
}
}
});
return ttlRemainingFuture;
}
续期代码
private void renewExpiration() {
Timeout task = commandExecutor.getConnectionManager().newTimeout(new TimerTask() {
@Override
public void run(Timeout timeout) throws Exception {
// 调用lua脚本进行续期
RFuture<Boolean> future = renewExpirationAsync(threadId);
future.onComplete((res, e) -> {
// 报异常就移除key
if (e != null) {
}
// 续期成功的话就递归自己
if (res) {
renewExpiration();
} else {
// 续期失败的话就取消续期,移除key等操作
cancelExpirationRenewal(null);
}
});
}
// 续期线程在过期时间达到三分之一的时候会进行续期
}, internalLockLeaseTime / 3, TimeUnit.MILLISECONDS);
ee.setTimeout(task);
}
续期保证原子性所以是lua代码
protected RFuture<Boolean> renewExpirationAsync(long threadId) {
return evalWriteAsync(getRawName(), 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(getRawName()),
internalLockLeaseTime, getLockName(threadId));
}