redisson 能干嘛

为什么要用分布式锁 redisson ,不用分布式锁 redisson 我们需要解决哪些问题?

  1. 单机版的服务使用SynchronizeLock是没问题的,如果一上集群,每个服务各自加锁的对象是不一样的,这时我们加的锁就不能在各自的服务里,需要借助外力,redis 进场;
  2. 把锁放在redis后,如果服务突然挂了,锁就永远解不了,所以必须在加锁的时候设置过期时间,且加锁操作和设置过期时间必须是原子操作;stringRedisTemplate.opsForValue().setIfAbsent(key, value, timeout, TimeUnit.SECONDS);
  3. 我们知道解锁的方法必须写在finally里面,但可能业务处理时间过长,锁到了过期时间自动解锁了,而业务处理完后又执行了finally里面的解锁方法,这时是解了别人的锁,A线程解B线程的锁,B线程解C线程的锁,后果不堪设想;所以还要判断 key(锁)里面的value是否等于我们拿到锁时value,如果相等才能删除;从拿到锁到比较value,到删除锁必须是原子操作,所以需要写LUR脚本或者用redis的事务,加上CAS自旋,一直比较和判断;
  4. 我们再来看集群 redis 的问题,我们知道 redis 是 AP 的,即高可用和分区容错性,没有强一致性;假如有3个redis节点ABC,程序加锁到A主节点后,A节点突然挂机了,没法将这个key同步到其他BC节点,而重新投票后B节点变成主节点,这时第二个线程进来也拿到了锁,拿到锁后又为所欲为…
  5. 第3点中提到业务处理时间过长自动解锁的问题;我们需要自己编写自动续期的逻辑;

分布式锁 redisson 可以帮我们解决以上的问题;



分布式锁 redisson 的使用

1、引入依赖

<!-- 以后使用Redisson作为所有分布式锁,分布式框架等功能框架 -->
<dependency>
    <groupId>org.redisson</groupId>
    <artifactId>redisson</artifactId>
    <version>3.12.0</version>
</dependency>

2、配置类

@Configuration
public class MyRedissonConfig {

	/**
	 * 所有对Redisson的使用都是通过RedissonClient
	 */
	@Bean(destroyMethod="shutdown")
	public RedissonClient redisson(){
		//1、创建配置
		Config config = new Config();
		config.useSingleServer().setAddress("redis://127.0.0.1:6379");

		//2、根据Config创建出RedissonClient实例
		RedissonClient redisson = Redisson.create(config);
		return redisson;
	}
}

3、测试:

@RunWith(SpringRunner.class)
@SpringBootTest
public class ApplicationTests {
    
	@Autowired
	private StringRedisTemplate stringRedisTemplate;

	@Autowired
	private RedissonClient redisson;

	@Test
	public void redissonTest() {
		System.out.println(redisson);
	}
}

//打印
org.redisson.Redisson@30dd00b6



redission 自动续期,看门狗原理

模拟业务超时:

@GetMapping(value = "/hello")
@ResponseBody
public String hello() {
    //1、获取一把锁,只要锁的名字一样,就是同一把锁
    RLock lock = redisson.getLock("my-lock");
    //2、加锁 默认加锁时间30s
    lock.lock(); 
    try {
        System.out.println("加锁成功,执行业务..."  + Thread.currentThread().getId());
        TimeUnit.SECONDS.sleep(20);
    } catch (InterruptedException e) {
        e.printStackTrace();
    } finally {
        //3、解锁
        System.out.println("释放锁..." + Thread.currentThread().getId());
        lock.unlock();
    }

    return "hello";
}

lock.lock(); 是阻塞式等待的,默认加锁时间是30s;如果业务超长,运行期间会自动续期到30s。不用担心业务时间长,锁自动过期被删掉;加锁的业务只要运行完成,就不会给当前锁续期,即使不手动解锁,锁默认会在30s内自动过期,不会产生死锁问题;

也可以自己指定解锁时间lock.lock(10,TimeUnit.SECONDS),10秒钟自动解锁,自己指定解锁时间redis不会自动续期;

指定解锁时间带来的问题:如果业务执行的时间超过指定时间,redis会自动解锁;当前业务执行完后又要解锁,可能会解锁到另一条线程加的锁,所以自己指定的解锁时间一定大于业务执行的时间!

看门狗原理

1、如果我们指定了锁的超时时间,就发送给redis执行脚本,进行占锁,默认超时就是我们制定的时间,不会自动续期;
2、如果我们未指定锁的超时时间,就使用 lockWatchdogTimeout = 30 * 1000 【看门狗默认时间】

只要占锁成功,就会启动一个定时任务【重新给锁设置过期时间,新的过期时间就是看门狗的默认时间】,每隔10秒都会自动再续成30秒;
自动续期时间:internalLockLeaseTime 【看门狗时间 30s】 / 3, 10s