redisson看门狗机制

官网解释

Redisson内部提供了一个监控锁的看门狗,它的作用是在Redisson实例被关闭前,不断的延长锁的有效期。默认情况下,看门狗的检查锁的超时时间是30秒钟,也可以通过修改Config.lockWatchdogTimeout来另行指定。

看门狗开启条件

我们可以看到,leaseTime != -1时,只执行tryLockInnerAsync方法,其它情况会执行下面的代码,而leaseTime 就是我们调用
lock(10, TimeUnit.SECONDS);方法传入的时间参数。

由此可知:redisson如果只是用lock.lock();不传过期时间的话,会启动看门狗机制,传过期时间的话,就不会启动看门狗机制。

// org.redisson.RedissonLock#tryAcquireAsync
private <T> RFuture<Long> tryAcquireAsync(long waitTime, long leaseTime, TimeUnit unit, long threadId) {
    if (leaseTime != -1) {
        return tryLockInnerAsync(waitTime, leaseTime, unit, threadId, RedisCommands.EVAL_LONG);
    }
    RFuture<Long> ttlRemainingFuture = tryLockInnerAsync(waitTime,
                                            commandExecutor.getConnectionManager().getCfg().getLockWatchdogTimeout(),
                                            TimeUnit.MILLISECONDS, threadId, RedisCommands.EVAL_LONG);
    // future模式,抢到锁之后开启看门狗
    ttlRemainingFuture.onComplete((ttlRemaining, e) -> {
        if (e != null) {
            return;
        }

        // lock acquired
        if (ttlRemaining == null) {
            scheduleExpirationRenewal(threadId); // 开启看门狗
        }
    });
    return ttlRemainingFuture;
}

看门狗如何开启的

以下代码就是开启看门狗的方法,我们可以看到,启动了一个TimerTask进行倒计时,默认倒计时时间为internalLockLeaseTime / 3,也就是默认的10秒钟(默认过期时间是30秒)。

// org.redisson.RedissonLock#renewExpiration
private void renewExpiration() {
     ExpirationEntry ee = EXPIRATION_RENEWAL_MAP.get(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;
             }
             // 关键方法,使用lua脚本刷新过期时间
             RFuture<Boolean> future = renewExpirationAsync(threadId);
             future.onComplete((res, e) -> {
                 if (e != null) {
                     log.error("Can't update lock " + getName() + " expiration", e);
                     return;
                 }
                 
                 if (res) {
                     // reschedule itself
                     renewExpiration(); // 不断地调用自己,刷新过期时间
                 }
             });
         }
     }, internalLockLeaseTime / 3, TimeUnit.MILLISECONDS);
     
     ee.setTimeout(task);
 }

看门狗的性能问题

很多小伙伴都认为看门狗是非常消耗性能的,其实性能的确是会有一些消耗,但是没有很多。
前几天有个小伙伴抛出了一个疑问:假如说每个线程都启动一个TimerTask来不断刷新过期时间,岂不是服务器很快就“炸了”?

其实不然,只有抢占到锁的线程才会开启看门狗,并不是每个等待的线程都会开启一个看门狗。也就是说——基本上每一个锁的单位会对应一个看门狗,而不是每一个线程对应一个看门狗。

这样看来,是不是性能浪费的就不是很多了?

其实看门狗机制主要是用于业务代码执行时间忽长忽短的,如果一个业务代码,我们确定它在10秒钟之内就会执行完毕,完全可以取消这个看门狗机制,来提升一部分性能。

举例来说

String lockKey = "product_001";//商品id作为锁
RLock lock = redisson.getLock(lockKey);
try{
    System.out.println("准备尝试获取锁");
    lock.lock(30, TimeUnit.SECONDS);

    System.out.println("进来了,开始停8秒");

    Thread.sleep(8000);
    //这里写逻辑
} catch (InterruptedException e) {
    e.printStackTrace();
} finally {
    lock.unlock();
}

以上是一个redisson锁的实现逻辑。我们可以看到,锁住的key是商品id。

假如说product_001商品被锁住了,在同一时间访问product_001商品的线程有且只能最多有一个,所以redisson看门狗在product_001商品上有且最多有一个。

但是商品并不只有product_001,可能有成百上千个商品,此时如果有大批量客户访问这成百上千个商品的话,那么就会生成成百上千个看门狗!这是个很恐怖的事情。

所以,看门狗能不用还是不用了。。。

强大的redisson

redisson非常强大,完美的解决了分布式系统下的很多问题。
具体请查阅文档:
redisson使用全解——redisson官方文档+注释(上篇)
redisson使用全解——redisson官方文档+注释(中篇)
redisson使用全解——redisson官方文档+注释(下篇)

Logo

为开发者提供学习成长、分享交流、生态实践、资源工具等服务,帮助开发者快速成长。

更多推荐