使用spring-integration快速实现mysql分布锁
Posted 水中加点糖
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了使用spring-integration快速实现mysql分布锁相关的知识,希望对你有一定的参考价值。
正如上篇文章spring自带分布式锁-[jdbc-lock-registry]源码解析所说的那样:
对于并发不是很高,并对性能追求不是很迫切,又不想引入其他第三方组件的情况下,使用数据库来实现分布式锁也是一种很不错的实现方式。
这里将快速演示如何在常见的spring boot项目中,通过引入spring-integration来快速实现一个mysql的分布式锁。
依赖引入
首先在maven中引入所需要的依赖
<!--mysql数据库驱动-->
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<scope>runtime</scope>
</dependency>
<!-- 引入jdbc支持 -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-jdbc</artifactId>
</dependency>
<!--分布式锁依赖-begin-->
<dependency>
<groupId>org.springframework.integration</groupId>
<artifactId>spring-integration-core</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.integration</groupId>
<artifactId>spring-integration-jdbc</artifactId>
</dependency>
<!--分布式锁依赖-end-->
主要是spring-integration-core和spring-integration-jdbc这两个依赖
分布式锁数据表初始化
如果使用过spring-task,那么一定会看到spring-task在启动前会创建一个名为TASK_LOCK的数据表,在spring-task中此表的创建是通过org.springframework.cloud.task.repository.support.TaskRepositoryInitializer这个类来创建的,其sql资源文件定义的路径为:
private static final String DEFAULT_SCHEMA_LOCATION = “classpath:org/springframework/”
+ “cloud/task/schema-@@platform@@.sql”;
根据项目配置的数据源,如果是mysql,那么它最终加载的是下面截图这个位置的sql脚本文件:
其分布式锁表TASK_LOCK的创建语句如下:
CREATE TABLE TASK_LOCK (
LOCK_KEY CHAR(36) NOT NULL,
REGION VARCHAR(100) NOT NULL,
CLIENT_ID CHAR(36),
CREATED_DATE DATETIME(6) NOT NULL,
constraint LOCK_PK primary key (LOCK_KEY, REGION)
) ENGINE=InnoDB;
得到sql语句后将上面的表ddl语句在对应的数据库中执行即可
分布式锁代码编写
完成了所需代码的依赖的引入,也完成了分布式表的初始化,那么接下来就可以直接写代码了。
此处以一个spring boot项目中的controller接口为例,实现对一个资源的执行加上分布式锁。
整体代码如下:
jdbcLockRegistry配置
@Configuration
public class JdbcLockRegistryConfig {
@Value("${server.port}")
private Integer port;
@Autowired
@Bean
public LockRegistry getDefaultLockRegistry(DataSource dataSource) throws Exception {
DefaultLockRepository lockRepository = new DefaultLockRepository(dataSource,
getLocalIpPort());
//设置分布式锁表的前缀,默认为INT_
lockRepository.setPrefix("TASK_");
//设置分布式锁的过期时间,单位为毫秒,默认为10秒
lockRepository.setTimeToLive(10_000);
//调用方法对锁的变量进行初始化
lockRepository.afterPropertiesSet();
return new JdbcLockRegistry(lockRepository);
}
/**
* 获取本地ip地址
*/
private String getLocalIpPort() throws Exception {
return InetAddress.getLocalHost().getHostAddress() + ":" + port;
}
}
测试controller代码
@RestController
@RequestMapping(value = "biz")
public class BizController {
@Resource
private LockRegistry lockRegistry;
/**
* @param id 资源id
*/
@GetMapping(value = "test.do")
public String test(String id) throws Exception {
Lock lock = lockRegistry.obtain(id);
//获取锁
if (lock.tryLock()) {
try {
for (int i = 0; i < 100; i++) {
Thread.sleep(1_000);
System.out.println(Thread.currentThread().toString() + " " + i);
}
} finally {
//释放锁
lock.unlock();
}
return System.currentTimeMillis() + "_" + id;
} else {
return System.currentTimeMillis() + "_" + id + "\\t" + Thread.currentThread() + " 获取锁失败";
}
}
}
application.properties配置文件
server.port=8080
spring.datasource.url=jdbc:mysql://192.168.1.2:3306/biz?useUnicode=true&characterEncoding=UTF-8&useSSL=false&serverTimezone=GMT%2B8
spring.datasource.username=root
spring.datasource.password=123456
spring.datasource.driver-class-name=com.mysql.cj.jdbc.Driver
配置文件中仅定义一个数据源即可
测试
然后做个测试:
初始状态,资源未被占用的情况访问:
其mysql中锁表内容情况如下:
资源已被占用的情况访问:
超过锁ttl还未释放锁测试:
总结
通过上面的测试我们可知,spring自带的jdbc版本的分布式锁可以快速完成分布式锁的开发。
在使用时对于锁的ttl时间建议设成最大值,因为其默认没有看门狗来给锁续期。
如想让其具有看门狗功能,可以参考下spring cloud task的源码。
以上是关于使用spring-integration快速实现mysql分布锁的主要内容,如果未能解决你的问题,请参考以下文章
使用spring-integration快速实现mysql分布锁
在 spring-integration 中使用有效负载类型路由器通过列表通用有效负载路由消息
Spring-Integration:将 DirectChannel 更改为 ExecutorChannel 结果为 ClassCastException
Spring-integration / ActiveMQ 在单个线程中订阅多个目的地