Springboot定时任务集成shedLock锁

0、写在前面

  最近在项目开发过程中,涉及到定时任务的编写,定时任务大家都知道,在多服务器部署时,为了防止同一时间同一任务多次执行的问题,通常需要使用分布式定时任务进行处理,这部分对应的框架也很多,例如:xxl-job,power-job,elastic-job,但是由于考虑到当前所负责开发的项目体量,感觉使用上面所提到的分布式定时任务框架太过于繁重,所以,这里就使用了spring提供的scheduler定时任务注解方式开发,虽然此种方式开发简便,但是同一时间同一任务多次执行的问题还是存在的,因此为了解决此问题,这里便引入了shedlock锁组件。

  关于shedlock,这里机翻了官网的一段说明,仅供简单介绍,详细可参看官网介绍(官网地址:https://github.com/lukas-krecan/ShedLock),官网说,shedlock只做一件事,就是确保计划任务最多同时执行一次,如果正在一个节点上执行任务,它将获取一个锁,以防止从另一个节点(或线程)执行相同任务。请注意,过去一个任务已在一个节点上执行,则其他节点上的执行不会等待,只会跳过它;目前支持Mongo、JDBC数据库、redis、hazelcast或zookeeper等存储,shedlock不是分布式调度框架,它只是一个锁!!!

1、版本说明

Spring Cloud Alibaba版本:2022.0.0.0-RC1
Springboot版本:3.0.0
jdk版本:17.0.6
ShedLock版本:5.3.0
mysql版本:8.0.30

2、ShedLock使用

  由于项目使用的数据库为MySQL数据库,所以这里使用了shedlock-jdbcTemplate方式,关于其他存储方式的使用可以参看官网(官网地址:https://github.com/lukas-krecan/ShedLock

2.1、创建数据库

CREATE TABLE shedlock(name VARCHAR(64) NOT NULL COMMENT '锁名称', 
                      lock_until TIMESTAMP(3) NOT NULL COMMENT '释放锁时间',
                      locked_at TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP(3) COMMENT '获取锁时间',
                      locked_by VARCHAR(255) NOT NULL COMMENT '锁提供者', 
                      PRIMARY KEY (name)
                     );

官网说明:

2.2、pom依赖

<properties>
    <shedlock.version>5.3.0</shedlock.version>
</properties>       

<dependency>
    <groupId>net.javacrumbs.shedlock</groupId>
    <artifactId>shedlock-core</artifactId>
    <version>${shedlock.version}</version>
</dependency>
<dependency>
    <groupId>net.javacrumbs.shedlock</groupId>
    <artifactId>shedlock-spring</artifactId>
    <version>${shedlock.version}</version>
</dependency>
<dependency>
    <groupId>net.javacrumbs.shedlock</groupId>
    <artifactId>shedlock-provider-jdbc-template</artifactId>
    <version>${shedlock.version}</version>
    <exclusions>
        <exclusion>
            <artifactId>spring-jdbc</artifactId>
            <groupId>org.springframework</groupId>
        </exclusion>
    </exclusions>
</dependency>
<dependency>
    <groupId>org.springframework</groupId>
    <artifactId>spring-jdbc</artifactId>
    <version>6.0.6</version>
</dependency>

说明:

这里单独引入spring-jdbc依赖的原因如下:

  由于Springboot 3.0.0版本使用的Spring 6.0.2版本在针对数据表中存在数据插入时,会报Duplicate key violation gets translated to DataIntegrityViolationException instead of DuplicateKeyException异常(issue地址:https://github.com/spring-projects/spring-framework/issues/29511),导致shedlock执行时,报类似异常,针对此问题,spring官方也进行了修复(https://github.com/spring-projects/spring-framework/releases/tag/v6.0.3),但是由于项目依赖管理的父pom定义了spring的版本,这里不方便修改父pom的依赖信息,所以就在项目中单独添加了spring-jdbc的依赖,已解决此异常。异常信息如下:

 Spring官方修复信息:

2.3、添加配置类

@Configuration
public class SchedulerLockConfig {
    @Bean
    public LockProvider lockProvider(DataSource dataSource) {
        return new JdbcTemplateLockProvider(
                JdbcTemplateLockProvider.Configuration.builder()
                        .withJdbcTemplate(new JdbcTemplate(dataSource))
                        .build()
        );
    }
}

2.4、启动类配置

在Springboot启动类上添加如下注解:

@EnableScheduling
@EnableSchedulerLock(defaultLockAtMostFor = "PT30S")

说明:

  defaultLockAtMostFor为锁默认的最大锁定时间,可以根据需要赋值,其中S为秒,M为分钟,H为小时,D为天,PT为ISO-8601期限格式,这部分可以参看Duration.parse()文档

2.5、定时任务方法配置

import net.javacrumbs.shedlock.spring.annotation.SchedulerLock;

...

@Scheduled(cron = "0 */2 * * * ?")
@SchedulerLock(name = "scheduledTaskName", lockAtLeastFor = "PT60S", lockAtMostFor = "PT60S")
@Transactional(rollbackFor = Exception.class)
public void scheduledTask() {
    // 代码省略......
}

shedlock数据表数据:

2.6、注解说明

@SchedulerLock:

  使用此注解的方法被锁定,通过指定锁的名称name属性(锁名称唯一),保证同一时间只能执行一个任务

关于lockAtLeastFor、lockAtMostFor属性说明:

  当第一个微服务执行定时任务的时候,会将此定时任务进行锁操作,然后其他的定时任务就不会再执行,锁操作有一定的时长,超过这个时长以后,锁释放,然后所有的定时任务进行争抢下一个定时任务的执行权利,如此循环。其中两个配置lockAtMostFor和lockAtLeastFor,保证了在一个定时任务的区间内只有一个定时任务在执行,同时也保证了即便是其中的一个定时任务挂掉了,到一定的时间以后,锁也会释放,其他的定时任务依旧会进行执行权的争夺,执行定时任务。

posted @ 2023-06-14 14:15  星空流年  阅读(517)  评论(0编辑  收藏  举报