RedisTemplate实现锁超时时间延长(模仿Redisson看门狗机制)

业务场景:

上一篇-Java业务功能并发问题处理的最后,我们用RedisTemplate实现了一个分布式锁,但是后面又有用户反馈同个单据出现了重复操作,让我们回忆下上次的加锁代码:
分布式锁请求锁逻辑


问题描述:

原因出现在我们锁住的那段代码执行了太久,超过预设的过期时长。在A线程还在执行中时,Redis中作为锁的key已经过期了,当B线程进入时判断已经没有锁了,因此允许执行。

解决方案分析:

由于我们不知道业务需要执行多久,需要一个机制在过期前检查是否线程还在运行中(为什么必须是过期前?因为过期了我们就无法知道线程是超时导致的解锁还是线程主动的解锁。),如果线程仍在运行,则将过期时间延长。下面绘制一个流程图让大家更直观地了解整个流程,此处是模仿Redisson看门狗机制
仿造看门狗机制流程图

代码实现:

// ...省略循环等代码
isSuccessLock = redisTemplate.opsForValue().setIfAbsent(redisKey, Thread.currentThread().getName(), lockTime, TimeUnit.MILLISECONDS);
if (Boolean.TRUE.equals(isSuccessLock)) {
	// 新增的重置过期时间方法 在加锁成功后, 开启1个线程, 做redis看门狗机制
    renewExpiration(lockKey, Thread.currentThread().getName(), lockTime);
    return true;
}
// 省略for循环中的try...catch代码

renewExpiration刷新锁时间的方法实现

/**
 * lua脚本 更新锁时间, 如果已经获取不到值, 则不更新锁的过期时间
 */
private static final String REDIS_RENEW_LOCK_SCRIPT = "if redis.call('get', KEYS[1]) == ARGV[1] " +
		"then return redis.call('expire', KEYS[1], ARGV[2]) " +
		"else return 0 end";
/**
 * 刷新锁时间
 * @param lockKey   锁key
 * @param requestId 锁线程号,redis的value
 * @param lockTime  锁时长
 */
public void renewExpiration(String lockKey, String requestId, long lockTime) {
	DefaultRedisScript<Long> redisScript = new DefaultRedisScript<>(REDIS_RENEW_LOCK_SCRIPT, Long.class);
	// 通过线程池创建异步线程
	taskExecutor.execute(() -> {
		while (true) {
			try {
				// 等待锁时长的1/3后刷新锁的过期时间
				Thread.sleep(lockTime / 3 * 1000);
			} catch (InterruptedException e) {
				Thread.currentThread().interrupt();
				break;
			}
			// 当前template只能传字符串, 将时间转为字符串传入
			String timeStr = String.valueOf(lockTime);
			// 延长过期时间原子操作
			Long execute = redisTemplate.execute(redisScript, new ArrayList<>(Collections.singleton(lockKey)), requestId, timeStr);
			if (execute == null || execute == 0) {
				break;
			}
		}
	});
}

总结:

其实后面才了解到Redis的分布式锁直接引入Redisson就万事大吉了,建议大家直接通过Redisson去做Redis的分布式锁,省时省力,方便完善。此处自己写其实也是为了更深入了解分布式锁的实现,而且一开始觉得就一个类解决的事情,引入多余的一个工具包会不会有点多余,结果做到后面才发现也有一些坑是自己没考虑周到的。等我后面有时间一定直接引入Redisson

参考链接:

redission的看门狗机制及应用

相关推荐

  1. Redisson - 看门机制(Watch Dog)禁用方案

    2024-03-13 03:52:03       36 阅读
  2. Redisson 实现分布式

    2024-03-13 03:52:03       49 阅读

最近更新

  1. docker php8.1+nginx base 镜像 dockerfile 配置

    2024-03-13 03:52:03       98 阅读
  2. Could not load dynamic library ‘cudart64_100.dll‘

    2024-03-13 03:52:03       106 阅读
  3. 在Django里面运行非项目文件

    2024-03-13 03:52:03       87 阅读
  4. Python语言-面向对象

    2024-03-13 03:52:03       96 阅读

热门阅读

  1. MogDB/openGauss关于PL/SQL匿名块调用测试

    2024-03-13 03:52:03       38 阅读
  2. 从菜鸟到大师细看程序员的五种层次

    2024-03-13 03:52:03       40 阅读
  3. 抓包是什么?我们为什么要抓包?

    2024-03-13 03:52:03       39 阅读
  4. Vue中怎么使用router进行页面传参

    2024-03-13 03:52:03       45 阅读
  5. 用游戏面试应聘者的方法

    2024-03-13 03:52:03       45 阅读
  6. 继续与否的决策

    2024-03-13 03:52:03       34 阅读
  7. 区块链技术的应用场景和优势

    2024-03-13 03:52:03       35 阅读
  8. Rust 注释用法

    2024-03-13 03:52:03       49 阅读
  9. 【video】记录audio+video

    2024-03-13 03:52:03       37 阅读