Redisson里的“看门狗”居然有这两个“bug”?

​大家好!

提到分布式锁,大家一般都会想到 Redis。

想到 Redis,一部分同学会说到 Redisson。

那么说到 Redisson,就不得不掰扯掰扯一下它的“看门狗”机制了。

所以你以为这篇文章我要给你讲“看门狗”吗?

不是,我主要是想给你汇报一下我最近研究的由于引入“看门狗”之后,给 Redisson 带来的两个看起来就菊花一紧的 bug :

  • 看门狗不生效的 BUG。

  • 看门狗导致死锁的 BUG。

为了能让你丝滑入戏,我还是先简单的给你铺垫一下,Redisson 的看门狗到底是个啥东西。

看门狗描述

你去看 Redisson 的 wiki 文档,在锁的这一部分,开篇就提到了一个单词:watchdog

github.com/redisson/re…

watchdog,就是看门狗的意思。

它是干啥用的呢?

好的,如果你回答不上来这个问题。那当你遇到下面这个面试题的时候肯定懵逼。

面试官:请问你用 Redis 做分布式锁的时候,如果指定过期时间到了,把锁给释放了。但是任务还未执行完成,导致任务再次被执行,这种情况你会怎么处理呢?

这个时候,99% 的面试官想得到的回答都是看门狗,或者一种类似于看门狗的机制。

如果你说:这个问题我遇到过,但是我就是把过期时间设置的长一点。

时间到底设置多长,是你一个非常主观的判断,设置的长一点,能一定程度上解决这个问题,但是不能完全解决。

所以,请回去等通知吧。

或者你回答:这个问题我遇到过,我不设置过期时间,由程序调用 unlock 来保证。

好的,程序保证调用 unlock 方法没毛病,这是在程序层面可控、可保证的。但是如果你程序运行的服务器刚好还没来得及执行 unlock 就宕机了呢,这个你不能打包票吧?

这个锁是不是就死锁了?

所以......

为了解决前面提到的过期时间不好设置,以及一不小心死锁的问题,Redisson 内部基于时间轮,针对每一个锁都搞了一个定时任务,这个定时任务,就是看门狗。

在 Redisson 实例被关闭前,这个狗子可以通过定时任务不断的延长锁的有效期。

因为你根本就不需要设置过期时间,这样就从根本上解决了“过期时间不好设置”的问题。默认情况下,看门狗的检查锁的超时时间是 30 秒钟,也可以通过修改参数来另行指定。

如果很不幸,节点宕机了导致没有执行 unlock,那么在默认的配置下最长 30s 的时间后,这个锁就自动释放了。

那么问题来了,面试官紧接着来一个追问:怎么自动释放呢?

这个时候,你只需要来一个战术后仰:程序都没了,你觉得定时任务还在吗?定时任务都不在了,所以也不会存在死锁的问题。

搞 Demo

前面简单介绍了原理,我也还是给你搞个简单的 Demo 跑一把,这样更加的直观。

引入依赖,启动 Redis 什么的就不说了,直接看代码。

示例代码非常简单,就这么一点内容,非常常规的使用方法:

把项目启动起来,触发接口之后,通过工具观察 Redis 里面 whyLock 这个 key 的情况,是这样的:

你可以看到在我的截图里面,是有过期时间的,也就是我打箭头的地方。

然后我给你搞个动图,你仔细看过期时间(TTL)这个地方,有一个从 20s 变回 30s 的过程:

 

​首先,我们的代码里面并没有设置过期时间的动作,也没有去更新过期时间的这个动作。

那么这个东西是怎么回事呢?

很简单,Redisson 帮我们做了这些事情,开箱即用,当个黑盒就完事了。

接下来我就是带你把黑盒变成白盒,然后引出前面提到的两个 bug。

我的测试用例里面用的是 3.16.0 版本的 Redission,我们先找一下它关于设置过期动作的源码。

首先可以看到,我虽然调用的是无参的 lock 方法,但是它其实也只是一层皮而已,里面还是调用了带入参的 lock 方法,只不过给了几个默认值,其中 leaseTime 给的是 -1:

而有参的 lock 的源码是这样的,主要把注意力放到我框起来的这一行代码中:

tryAcquire 方法是它的核心逻辑,那么这个方法是在干啥事儿呢?

点进去看看,这部分源码又是这样的:

其中 tryLockInnerAsync 方法就是执行 Redis 的 Lua 脚本来加锁。

既然是加锁了,过期时间肯定就是在这里设置的,也就是这里的 leaseTime:

<

  • 8
    点赞
  • 26
    收藏
    觉得还不错? 一键收藏
  • 5
    评论
### 回答1: Redisson看门狗可以用来保护程序运行不中断。它可以帮助监视程序的运行状况,并在程序出现异常时自动重启。例如:// 创建Redisson实例 RedissonClient redisson = Redisson.create(); // 获取看门狗实例 RedissonReactiveWatchdogService watchdogService = redisson.getWatchdogService(); // 注册一个看门狗,如果超过10秒没有调用keepAlive(),将会触发回调函数 watchdogService.register(10, () -> System.out.println("Restarting application")); // 保持看门狗活跃 watchdogService.keepAlive(); ### 回答2: Redisson是一个基于Java的开源的分布式和响应式的Java数据结构和分布式服务框架,它提供了很多用于处理分布式场景的高性能可扩展组件。 Redisson看门狗(Watchdog)是一种监视工具,用于监控Redis连接是否处于活动状态,并在连接不活动时进行自动重连。 使用Redisson看门狗需要以下步骤: 1. 首先,需要创建一个Redisson客户端实例,可以使用Redisson提供的Redisson.create()方法来创建: ``` Config config = new Config(); config.useSingleServer().setAddress("redis://127.0.0.1:6379"); RedissonClient redissonClient = Redisson.create(config); ``` 2. 创建Redis连接管理器: ``` ConnectionManager connectionManager = redissonClient.getConnectionManager(); ``` 3. 创建Redisson连接监听器: ``` RedisConnectionListener connectionListener = new RedisConnectionListener() { @Override public void onConnect(InetSocketAddress addr) { // 连接成功时的处理逻辑 } @Override public void onDisconnect(InetSocketAddress addr) { // 连接断开时的处理逻辑 } }; ``` 4. 创建看门狗实例并添加连接监听器: ``` RedissonWatchdog watchdog = new RedissonWatchdog(connectionManager); watchdog.addListener(connectionListener); ``` 5. 启动看门狗: ``` watchdog.start(); ``` 以上是使用Redisson看门狗的基本过程,当Redis连接断开时,看门狗会自动尝试重新建立连接,并在连接成功或失败时回调相应的方法。 需要注意的是,看门狗只能在Redisson客户端关闭之前进行启动,一旦启动后将在后台运行。 另外,为了保证看门狗的正确运行,需要使用try-catch语句块来捕获可能出现的异常并进行处理。 此外,还可以通过watchdog.setConnectionMinimumIdleSize(int minSize)方法设置最小空闲连接数,以保证连接池中始终有一定数量的可用连接。 总之,通过Redisson看门狗,我们可以轻松地实现Redis连接的自动重连功能,提高了应用的可靠性和稳定性。 ### 回答3: Redisson是一个基于Java的Redis客户端,它提供了一个看门狗(Watcher)的功能,用于监听Redis服务的状态变化。 使用Redisson看门狗需要导入相关的依赖,例如: ```xml <dependency> <groupId>org.redisson</groupId> <artifactId>redisson</artifactId> <version>3.15.5</version> </dependency> ``` 接下来,可以通过如下的代码来创建一个RedissonClient对象,并设置监听器来监控Redis服务的状态变化: ```java import org.redisson.Redisson; import org.redisson.api.RedissonClient; import org.redisson.config.Config; import org.redisson.config.SingleServerConfig; public class RedissonWatcherExample { public static void main(String[] args) { // 创建Redisson配置对象 Config config = new Config(); SingleServerConfig singleServerConfig = config.useSingleServer() .setAddress("redis://127.0.0.1:6379") .setDatabase(0); // 创建RedissonClient对象 RedissonClient redisson = Redisson.create(config); // 添加监听器来监控Redis服务状态变化 redisson.getConnectionManager().getWatchdog() .addListener(new MyWatchdogListener()); // 使用RedissonClient进行操作 // ... // 关闭RedissonClient redisson.shutdown(); } // 自定义的看门狗监听器 private static class MyWatchdogListener implements ConnectionWatchdogListener { @Override public void onConnectivityChanged(boolean isConnected) { if (isConnected) { System.out.println("Redis服务已连接"); } else { System.out.println("Redis服务已断开连接"); } } } } ``` 上述代码中,通过`config.useSingleServer()`来指定Redis的连接信息,然后通过`Redisson.create(config)`来创建RedissonClient对象。接着,可以通过`redisson.getConnectionManager().getWatchdog().addListener()`方法来添加自定义的看门狗监听器,即`MyWatchdogListener`对象。在监听器中,可以根据`isConnected`参数来判断Redis服务的连接状态。 最后,可以使用`RedissonClient`对象进行其他的Redis操作。在程序结束时,可以调用`redisson.shutdown()`方法来关闭`RedissonClient`对象。 这是一个简单的Redisson看门狗使用的示例,通过监听Redis服务的连接状态变化,可以及时做出相应的处理。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 5
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值