Redisson watchdog principle

redission lock principle

  • watchDog will only take effect when the specified lock time (leaseTime) is not displayed. (This is an important point)
  • The time set for lockWatchdogTimeout should not be too small. For example, I set it to 100 milliseconds before. Because the network directly caused the lock to be completed and the watchdog was extended, the key had been deleted in redis.
  • When the lock method is called, tryAcquireAsync will eventually be called. The call chain is: lock()->tryAcquire->tryAcquireAsync. The detailed explanation is as follows:
private <T> RFuture<Long> tryAcquireAsync(long waitTime, long leaseTime, TimeUnit unit, long threadId) {
    
    
    RFuture<Long> ttlRemainingFuture;
    //如果指定了加锁时间,会直接去加锁
    if (leaseTime != -1) {
    
    
        ttlRemainingFuture = tryLockInnerAsync(waitTime, leaseTime, unit, threadId, RedisCommands.EVAL_LONG);
    } else {
    
    
        //没有指定加锁时间 会先进行加锁,并且默认时间就是 LockWatchdogTimeout的时间
        //这个是异步操作 返回RFuture 类似netty中的future
        ttlRemainingFuture = tryLockInnerAsync(waitTime, internalLockLeaseTime,
                                               TimeUnit.MILLISECONDS, threadId, RedisCommands.EVAL_LONG);
    }

    //这里也是类似netty Future 的addListener,在future内容执行完成后执行
    ttlRemainingFuture.onComplete((ttlRemaining, e) -> {
    
    
        if (e != null) {
    
    
            return;
        }

        // lock acquired
        if (ttlRemaining == null) {
    
    
            // leaseTime不为-1时,不会自动延期
            if (leaseTime != -1) {
    
    
                internalLockLeaseTime = unit.toMillis(leaseTime);
            } else {
    
    
                //这里是定时执行 当前锁自动延期的动作,leaseTime为-1时,才会自动延期
                scheduleExpirationRenewal(threadId);
            }
        }
    });
    return ttlRemainingFuture;
}

renewExpiration is called in scheduleExpirationRenewal. Here we can see that a timeout is enabled to perform delayed actions.

private void renewExpiration() {
    
    
        ExpirationEntry ee = EXPIRATION_RENEWAL_MAP.get(getEntryName());
   // 解锁操作会 EXPIRATION_RENEWAL_MAP.remove(getEntryName());
        if (ee == null) {
    
    
            return;
        }
 
        Timeout task = commandExecutor.getConnectionManager().newTimeout(new TimerTask() {
    
    
            @Override
            public void run(Timeout timeout) throws Exception {
    
    
                ExpirationEntry ent = EXPIRATION_RENEWAL_MAP.get(getEntryName());
                if (ent == null) {
    
    
                    return;
                }
                Long threadId = ent.getFirstThreadId();
                if (threadId == null) {
    
    
                    return;
                }
 
                RFuture<Boolean> future = renewExpirationAsync(threadId);
                future.onComplete((res, e) -> {
    
    
                    if (e != null) {
    
    
                        log.error("Can't update lock " + getRawName() + " expiration", e);
                        EXPIRATION_RENEWAL_MAP.remove(getEntryName());
                        return;
                    }
 
                    if (res) {
    
    
                        //如果 没有报错,就再次定时延期
                        // reschedule itself
                        renewExpiration();
                    } else {
    
    
                        cancelExpirationRenewal(null);
                    }
                });
            }
            // 这里我们可以看到定时任务 是 lockWatchdogTimeout 的1/3时间去执行 renewExpirationAsync
        }, internalLockLeaseTime / 3, TimeUnit.MILLISECONDS);
 
        ee.setTimeout(task);
    }
 protected RFuture<Boolean> renewExpirationAsync(long threadId) {
    
    
        return this.evalWriteAsync(this.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(this.getRawName()), this.internalLockLeaseTime, this.getLockName(threadId));

in conclusion

watch dog renews the key of the distributed lock for 30 seconds every 10 seconds when the current node is alive;
when the watch dog mechanism is started and there is no lock release operation in the code, watch dog will continue to renew the lock;
if the program releases the lock operation because If the exception is not executed, the lock cannot be released, so the lock release operation must be placed in finally {};
to make the watchLog mechanism effective, do not set the expiration time when locking.
The watchlog delay time can be specified by lockWatchdogTimeout. The default delay time, But don't set it too small. For example, 100
watchdog will delay every lockWatchdogTimeout/3 time.
watchdog implements asynchronous delay through netty-like Future function
watchdog ultimately uses Lua script to delay

Some thoughts on watchdogs

To put it simply, the watchdog mode is to start a daemon thread during the execution of the main thread/service task for tracking, progress monitoring and functional supplementation (delay, alarm, etc.). This mode can be used in other places where needed in the future. use. To be continued. .

Guess you like

Origin blog.csdn.net/qq_37436172/article/details/130656960