当前位置: 首页 >> 汽车
世界热文:Springboot定时任务集成shedLock锁
来源:博客园     时间:2023-06-14 15:07:28

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-RC1Springboot版本:3.0.0jdk版本:17.0.6ShedLock版本:5.3.0mysql版本: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依赖

    5.3.0           net.javacrumbs.shedlock    shedlock-core    ${shedlock.version}    net.javacrumbs.shedlock    shedlock-spring    ${shedlock.version}    net.javacrumbs.shedlock    shedlock-provider-jdbc-template    ${shedlock.version}                        spring-jdbc            org.springframework                org.springframework    spring-jdbc    6.0.6

说明:

这里单独引入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、添加配置类

@Configurationpublic 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,保证了在一个定时任务的区间内只有一个定时任务在执行,同时也保证了即便是其中的一个定时任务挂掉了,到一定的时间以后,锁也会释放,其他的定时任务依旧会进行执行权的争夺,执行定时任务。

标签: