使用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 在单个线程中订阅多个目的地

Spring-Integration Webflux 异常处理

使用Spring-Integration获取具有某些字段(投影)的mongodb文档(仅限注释)