首页 > 数据库 >使用 Spring Integration 实现基于 Redis 的分布式锁以及踩坑

使用 Spring Integration 实现基于 Redis 的分布式锁以及踩坑

时间:2023-09-26 11:34:08浏览次数:49  
标签:localLock get Spring redis Integration 获取 lock Redis 分布式

背景

分布式锁的应用场景应该还是蛮多的,这里就不赘述了。
之前在开发中实现分布式锁都是自己基于 Redis 造轮子,虽然也不复杂并且自己实现一次能对分布式锁有更深的了解,但是终归有些麻烦。尤其是新项目需要的时候还得 CV 一次。
然后在查询过程中(毫不意外地)发现 Spring 有现成的组件实现,所以决定拿过来使用一次,顺便了解一下。

使用

引入依赖

<dependency>
	<groupId>org.springframework.boot</groupId>
	<artifactId>spring-boot-starter-data-redis</artifactId>
</dependency>

<dependency>
	<groupId>org.springframework.boot</groupId>
	<artifactId>spring-boot-starter-integration</artifactId>
</dependency>

<dependency>
	<groupId>org.springframework.integration</groupId>
	<artifactId>spring-integration-redis</artifactId>
</dependency>

创建相关的 Bean

@Configuration
public class RedisLockConfig {

    @Bean
    public RedisLockRegistry redisLockRegistry(RedisConnectionFactory redisConnectionFactory) {
        return new RedisLockRegistry(redisConnectionFactory, "lock");
    }
}

相关代码示例

这里省略注入部分代码,仅展示使用部分

// 获取锁对象
Lock lock = redisLockRegistry.obtain("lock");

// 尝试上/获取锁
boolean isLocked = false;
try {
	// 1s 内持续尝试获取锁
	isLocked = lock.tryLock(1, TimeUnit.SECONDS);
} catch (InterruptedException e) {
	// 获取锁异常
	log.error("get lock fail", e);
	// ……
}

if (isLocked) {
// 获取锁成功
	try {
		// 业务逻辑
		log.info("get lock success");
	} catch (Exception e) {
		// 业务逻辑异常
		log.error("do sth fail", e);
	} finally {
	 	// 确保解锁
		lock.unlock();
	}
} else {
// 获取锁失败
	log.info("get lock fail");
	// ……
}

踩坑

问题描述

为了尽可能简单测试加锁效果,我的做法是创建一个获取锁不释放的接口,然后重复调用。这样第一次获取锁成功后,后面的调用应该在过期时间内是无法获取的。
上面都没什么问题,然后我尝试手动删掉 redis 里的锁记录之后发现还是锁着的。然后发现,甚至超时事件过去之后都依然是锁着的。

排查

当然很容易想到除了 redis,应该还会有其他地方保存者锁信息,这个通过打断点很容易发现:
image
很明显,在下面获取 redis 锁之前先尝试获取了 localLock 的锁,虽然 redis 的锁已经没了,但 localLock 仍然还在,所以依然被锁。
只有在 localLock 没被锁,能够获取锁的情况下才回去尝试获取 redis 的锁。然后如果获取 redis 锁失败(返回 false)的情况下又把 localLock 解锁……
emmm,感觉逻辑有点怪,不排除是个 BUG?之后再研究下吧。

解决

解决起来也很方便,随便 Google 一下就能找到一篇文档:
Distributed lock with Redis and Spring Boot

In some situations, you might need to acquire a lock but not release it. For example, you have a @Scheduled task that runs each 15 seconds on each instance and you don’t want it to be run more often than once per 15 seconds.
To do it you can get a lock and exit from a method without releasing. In such cases, I suggest calling lockRegistry.expireUnusedOlderThan(TTL) each time before obtaining a lock (actually it is better to call it for all cases). This method removes old not released locks from the map locks and prevents the situation when one instance has a map with old locks and all threads of this instance (except the one which acquired this lock) cannot acquire it.

按以上的介绍,建议每次获取锁前都调用一次lockRegistry.expireUnusedOlderThan(TTL)来避免问题,按这种方法来进行操作确实解决了该问题。
(不得不吐槽一句,使用中文检索到的相关博文没有看到有提到这一点的……)

标签:localLock,get,Spring,redis,Integration,获取,lock,Redis,分布式
From: https://www.cnblogs.com/cbc-onne/p/17729713.html

相关文章

  • springMVC
               ......
  • Spring框架
    1.OCP开闭原则什么是COP?COP是软件七大开发原则当中最基本的原则之一:开闭原则对扩展开放,对修改关闭。COP原则是最核心最基本的,其他六个原则都是为了这个原则服务的。COP开闭的原则核心是:只要当你在扩展系统功能的时候,没有修改之前写好的代码,那么就是符合COP原则的。反之,如果......
  • 基于springboot学生请假管理系统-计算机毕业设计源码+LW文档
    摘要:本学生请假管理系统是针对目前学生请假的实际需求,从实际工作出发,对过去的学生请假管理系统存在的问题进行分析,完善用户的使用体会。采用计算机系统来管理信息,取代人工管理模式,查询便利,信息准确率高,节省了开支,提高了工作的效率。本系统结合计算机系统的结构、概念、模型、原理......
  • 基于Spring的大学生竞赛活动平台-计算机毕业设计源码+LW文档
    摘要:本大学生课余休闲平台是针对目前大学生课余休闲平台的实际需求,从实际工作出发,对过去的大学生课余休闲平台存在的问题进行分析,完善用户的使用体会。采用计算机系统来管理信息,取代人工管理模式,查询便利,信息准确率高,节省了开支,提高了工作的效率。本系统结合计算机系统的结构、概......
  • Redis大key问题解决方案
     Redis的大key如何处理 介绍 大key并不是指key的值很大,而是key对应的value很大(非常占内存)一般而言,下面这两种情况被称为大key:String类型的值大于10KB;Hash、List、Set、ZSet类型的元素的个数超过5000个;为什么会出现大key数据结构不合理:当使用Re......
  • Spring Boot 目录遍历--表达式注入--代码执行--(CVE-2021-21234)&&(CVE-2022-22963)&&
    SpringBoot目录遍历--表达式注入--代码执行--(CVE-2021-21234)&&(CVE-2022-22963)&&(CVE-2022-22947)&&(CVE-2022-2296)SpringBoot目录遍历(CVE-2021-21234)漏洞简介spring-boot-actuator-logview是一个简单的日志文件查看器作为SpringBoot执行器端点,在0.2.13版本之前存......
  • SpringBoot 整合 Devtools 热部署工具
    什么是热部署实际开发过程中,修改应用的业务逻辑代码时常常需要重启应用,这显得非常繁琐,降低了开发效率,所以热部署对于开发来说显得十分必要。应用启动后会把编译好的Class文件加载到虚拟机中,正常情况下载项目修改了Java源文件是需要全部重新编译并加载(需要重启应用),而热部署......
  • Spring Boot RestController接口如何输出到终端
    背景公司项目的批处理微服务,一般是在晚上固定时段通过定时任务执行,但为了预防执行失败,我们定义了对应的应急接口,必要时可以通过运维在终端中进行curl操作。然而,部分任务耗时较长,curl命令执行后长时间没有输出,如果不查看日志,无法知道系统当前的状态,因此有必要研究一下如何在curl命......
  • Spring Security 基于 JWT Token 的接口安全控制
    现在的网站开发,基本上都是前后端分离,后端提供api接口并进行权限控制。使用SpringSecurity框架可以大大简化权限控制的代码实现。对于后端接口而言,为了能够实现多节点负载均衡部署,更好的方案是不再使用Session了,绝大多数情况下,通过提交JWTToken来进行身份认证。本篇博客......
  • SpringCloud之Gateway
    1.什么是GatewaySpringCloudGateway是Spring公司基于Spring5.0,SpringBoot2.0和ProjectReactor等术开发的网关,它旨在为微服务架构提供一种简单有效的统一的API路由管理方式。它的目标是替代NetflflixZuul,其不仅提供统一的路由方式,并且基于Filter链的方式提供了......