文章首发于有间博客,欢迎大伙光临Redis分布式锁业务没执行完但锁超时了怎么办? --Redisson
这个问题的思考源自JD的面试题,因为项目中有使用分布式锁,切切实实的被问到了这个问题,在面试中也没有答得很好,现在有空了来对问题进行一个解决思考和整理。
在项目中如果使用redis原生的分布式锁,setnx设置并使用expire定义超时时间,就有可能出现锁的时间到了但业务没有执行完毕的情况。那如果不设置超时时间,万一服务宕机没来得及解锁呢,那这个分布式锁就再也不能访问了。
由此,我们可以使用redis设置分布式锁的正确姿势 –Redisson,通过redisson我们就能安全的下车!
深入了解可以看Redisson 文档: redisson
我们可以根据redisson的可重入锁,来设置分布式锁,使用的方式和java中的ReentrantLock基本一致。并且redisson内部提供了一个监控锁的看门狗,在redisson实例被关闭前,会不断的延长锁的有效期,如果redisson实例被关闭了,那么锁时间到后自动关闭。
so,上述面试的问题也就可以有了答案了,我们可以使用redisson客户端进行分布式锁的设置,使用tryLock方法对分布式锁的设置进行判断,执行不同的逻辑,如果业务逻辑执行的过程中服务宕机,那么会由redisson提供的看门狗监控锁进行兜底,在实例结束后的一段时间内对锁进行清除。看门狗设置过期的时间默认是30s,每30 / 3 s进行一次判断,我们也可以对看门狗时间进行一个手动的设置。
在用redisson设置分布式锁的时候,如果想让看门狗监控锁生效,那么则不能指定锁的过期时间,不然无效,来看看官方文档是怎么说的。
在使用redisson之前需要先设置pom依赖和配置redisson的配置文件
<dependency> <groupId>org.redisson</groupId> <artifactId>redisson</artifactId> <version>3.11.1</version> </dependency>redisson配置
@Configuration public class redissonConfig { @Bean public RedissonClient configRedisson(){ Config config = new Config(); config.useSingleServer().setAddress("redis://172.0.0.1:6379") .setPassword("777777"); config.setCodec(new StringCodec()); //设置看门狗的时间,不配置的话默认30000 config.setLockWatchdogTimeout(12000); RedissonClient redisson = Redisson.create(config); return redisson; } }完毕后就可以在业务逻辑中引入调用。一个简单的小例子
@Autowired RedissonClient redisson; @GetMapping(value = "/getWatchDogLock") public String getWatchDog() throws InterruptedException { RLock watchDogLock = redisson.getLock("watchDogLock"); boolean judge; //进行3s的尝试时间,如果失败则返回false, 还可以设置锁过期时间,如果设置会导致看门狗无效 judge = watchDogLock.tryLock(3, TimeUnit.SECONDS); //输出是否能够获取到锁 System.out.println(Thread.currentThread().getName() + "的锁获取情况:" + judge); //如果获取到锁 if (judge){ try { System.out.println(Thread.currentThread().getName() + " 已经成功获取到的分布式锁" + System.currentTimeMillis()); //执行主要业务逻辑 Thread.sleep(6000); } catch (InterruptedException e) { e.printStackTrace(); }finally { //最后进行解锁操作,如果服务宕机,则到不了这一步,会等待看门狗监控锁结束锁清除 System.out.println(Thread.currentThread().getName() + " 进行解锁操作" + System.currentTimeMillis()); watchDogLock.unlock(); } }else { //如果没有获取到分布式锁,执行业务逻辑 System.out.println(Thread.currentThread().getName() + " 没有成功获取到分布式锁,锁已经被占用" + System.currentTimeMillis()); } //最后输出 if (judge){ return "成功获取到分布式锁"; } return "没有获取到分布式锁"; }在上述配置文件中设置了看门狗监控锁的时间为12s,在锁获取后我对服务进行了中断,来看看该锁的超时时间变化。
中断前:可以看到每当时间减少了1/3 就会重新恢复到12,也就是降低到8就会判断redisson实例是否存活,恢复时间。 中断后:中断后等待看门狗监控锁配置的时间结束后,分布式锁消失。
总结一下,在我们使用redisson客户端设置分布式锁的时候,如果担心有业务逻辑没有执行完毕而锁过期的情况,会使用看门狗监控锁进行兜底,防止服务的宕机。看门狗的时间可以在配置中使用LockWatchdogTimeout 进行设置,视业务情况而定。
如有错误的地方请在评论区指出探讨。