Redis-WatchDog源码分析

676 阅读1分钟

锁续期

  • 当代码还没有执行完,但是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));
}