Redisson看门狗机制详解

81 阅读2分钟

1. 测试代码,以黑马点评项目P67视频为基础


import lombok.extern.slf4j.Slf4j;  
import org.junit.jupiter.api.BeforeEach;  
import org.junit.jupiter.api.Test;  
import org.redisson.api.RLock;  
import org.redisson.api.RedissonClient;  
import org.springframework.boot.test.context.SpringBootTest;  
  
import javax.annotation.Resource;  
import java.util.concurrent.TimeUnit;  
@Slf4j  
@SpringBootTest  
class RedissonTest {  
    @Resource  
    private RedissonClient redissonClient;  
    private RLock lock;  
    @BeforeEach  
    void setUp() {  
        lock = redissonClient.getLock("order");  
    }  
    @Test  
    void method1() throws InterruptedException {  
        // 尝试获取锁  
        boolean isLock = lock.tryLock(1L, TimeUnit.SECONDS); //锁的过期时间默认为30s
        if (!isLock) {  
            log.error("获取锁失败 .... 1");  
            return;  
        }  
        try {  
            log.info("获取锁成功 .... 1");  
            method2();  
            log.info("开始执行业务 ... 1");  
        } finally {  
            log.warn("准备释放锁 .... 1");  
            lock.unlock();  
        }  
    }  
    void method2() {  
        // 尝试获取锁  
        boolean isLock = lock.tryLock();  
        if (!isLock) {  
            log.error("获取锁失败 .... 2");  
            return;  
        }  
        try {  
            log.info("获取锁成功 .... 2");  
            log.info("开始执行业务 ... 2");  
        } finally {  
            log.warn("准备释放锁 .... 2");  
            lock.unlock();  
        }  
    }  
}

2. 锁重试运行逻辑分析


点击查看lock.tryLock(1L, TimeUnit.SECONDS);的源码实现 在这里插入图片描述

在Long ttl=tryAcquire(waitTime,leaseTime,unit,threadId);尝试获取锁,开始进行获取锁的业务逻辑,点击进入tryAcquire函数 ![[Pasted image 20240118191114.png]]

在tryAcquire函数中调用了tryAcquireAsync函数,下面为tryAcquireAsync函数 ![[Pasted image 20240118191242.png]]

随后执行TryLockInnerAsync函数,通过执行lua脚本来保证原子性获取锁 ![[Pasted image 20240118192342.png]]

lua脚本解释:

  1. 判断锁是否存在,若不存在则设置锁,并将value设为1,设置过期时间
  2. 如果锁存在,判断是否是自己的锁,若是,则将value+1,重置过期时间
  3. 若不是自己的锁,则锁的剩余过期时间

该函数返回null(获取锁成功)或者锁的剩余过期时间,在tryLock函数中会进行判断,如果为null,则返回true 否则,则获取当前时间,订阅锁过期通知,订阅等待时间是有限的(值为剩余等待时间) ![[Pasted image 20240118193956.png]]

当在规定时间等到了锁过期通知,则会执行以下部分,仍会进行锁等待时间的判断 ![[Pasted image 20240118194302.png]]

之后便进行获取锁的重试 ![[Pasted image 20240118194427.png]]

业务流程图分析如下: ![[Pasted image 20240118201245.png]]

3. 锁超时运行逻辑分析


当线程1业务因为阻塞,导致锁超时释放,而线程2拿到了该锁,当线程1业务执行完毕,释放锁,会导致线程2的锁丢失,其他线程可以去获取锁

在执行ttlRemainingFuture.onComplete时,ttlRemaining表示剩余有效期,e表示异常 ![[Pasted image 20240118195532.png]]

当剩余有效期为null的时候,执行scheduleExpirationRenewal函数释放锁,下面为scheduleExpirationRenewal函数 ![[Pasted image 20240118195924.png]]

该函数会根据线程Id,进行创建锁,如果不是第一次进入,则更新有效期。在renewExpiration()函数中执行更新有效期,最终会执行lua脚本 ![[Pasted image 20240118200853.png]]

全流程的流程图如下图所示: ![[Pasted image 20240118201520.png]]