SpringBoot2.x整合轻量级分布式定时任务ShedLock的使用详解
一、关于ShedLock
ShedLock采用非侵入式编程的思想,通过注解的方式来实现相应的功能。ShedLock是一个在分布式环境中使用的定时任务框架,用于解决在分布式环境中的多个实例的相同定时任务在同一时间点重复执行的问题,解决思路是通过对公用的数据库中的某个表进行记录和加锁,使得同一时间点只有第一个执行定时任务并成功在数据库表中写入相应记录的节点能够成功执行而其他节点直接跳过该任务。当然不只是数据库,目前已经实现的支持数据存储类型除了经典的关系型数据库,还包括JDBC、Redis、MongoDB、CosmosDB、DynamoDB、以及分布式协调服务Zookeeper、还有ElasticSearch等等,是非常的丰富的。
详细集成步骤
- 在pom.xml中新增引入Spring整合ShedLock依赖包(基于JDBC来提供锁)
<!--分布式定时任务锁-->
<dependency>
<groupId>net.javacrumbs.shedlock</groupId>
<artifactId>shedlock-spring</artifactId>
<version>2.2.1</version>
</dependency>
<dependency>
<groupId>net.javacrumbs.shedlock</groupId>
<artifactId>shedlock-provider-jdbc-template</artifactId>
<version>2.2.1</version>
</dependency>
- 建表
DROP TABLE IF EXISTS `shedlock`;
CREATE TABLE `shedlock` (
`name` varchar(64) NOT NULL COMMENT '锁名称(name必须是主键',
`lock_until` timestamp(3) NULL DEFAULT NULL COMMENT '释放锁时间',
`locked_at` timestamp(3) NULL DEFAULT NULL COMMENT '获取锁时间',
`locked_by` varchar(255) DEFAULT NULL COMMENT '锁提供者',
PRIMARY KEY (`name`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
- 配置LockProvider,这儿以JDBC为实现的
import net.javacrumbs.shedlock.core.LockProvider;
import net.javacrumbs.shedlock.provider.jdbctemplate.JdbcTemplateLockProvider;
import net.javacrumbs.shedlock.spring.ScheduledLockConfiguration;
import net.javacrumbs.shedlock.spring.ScheduledLockConfigurationBuilder;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.scheduling.annotation.EnableScheduling;
import javax.sql.DataSource;
import java.time.Duration;
@Configuration
@EnableScheduling
public class ShedlockConfig {
@Bean
public LockProvider lockProvider(DataSource dataSource) {
return new JdbcTemplateLockProvider(dataSource);
}
@Bean
public ScheduledLockConfiguration scheduledLockConfiguration(LockProvider lockProvider) {
return ScheduledLockConfigurationBuilder
.withLockProvider(lockProvider)
.withPoolSize(10)
.withDefaultLockAtMostFor(Duration.ofMinutes(10))
.build();
}
}
- 在项目的启动类中新增 “ @EnableSchedulerLock ” 注解,开启ShedLock
import net.javacrumbs.shedlock.spring.annotation.EnableSchedulerLock;
import org.mybatis.spring.annotation.MapperScan;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.boot.web.servlet.ServletComponentScan;
import org.springframework.cloud.client.discovery.EnableDiscoveryClient;
import org.springframework.cloud.context.config.annotation.RefreshScope;
import org.springframework.scheduling.annotation.EnableAsync;
import org.springframework.scheduling.annotation.EnableScheduling;
import org.springframework.transaction.annotation.EnableTransactionManagement;
@EnableAsync
@SpringBootApplication
@RefreshScope
@EnableDiscoveryClient
@EnableTransactionManagement
@EnableScheduling
@ServletComponentScan
@EnableSchedulerLock(defaultLockAtMostFor = "PT20M", defaultLockAtLeastFor = "PT20M")
@MapperScan(value={"com.nari.platform.mapper"})
public class NariPlatform {
public static void main(String[] args) {
SpringApplication.run(NariPlatform.class, args);
}
}
- 在需要加锁的Scheduled任务上添加注解 @SchedulerLock
@Configuration
@EnableScheduling
@Slf4j
public class CalcDeviceDataTask {
@Scheduled(cron = "0 11 06 * * ?")
@SchedulerLock(name = "configureTasks", lockAtLeastFor = 120000, lockAtMostFor = 18000)
public void configureTasks() {
try{
log.info("开始执行定时任务"+System.currentTimeMillis());
} catch (Exception e){
log.info("定时任务执行失败"+e.getMessage());
}
}
}
参数说明:
@SchedulerLock 作用:只有某个方法上添加了该注解的方法才会被锁定,该库将忽略所有其他计划任务。且必须为锁指定名称,这样才能在同一时间只能有一个具有相同名称的任务执行,以达到预期的锁的目的。
参数 | 类型 | 描述 |
---|---|---|
name | String | 用来标注一个定时服务的名字,被用于写入数据库作为区分不同服务的标识,如果有多个同名定时任务则同一时间点只有一个执行成功。默认为:"" |
lockAtMostFor | $12 | 成功执行任务的节点所能拥有独占锁的最长时间,单位是毫秒ms。默认值为:-1L,表示不生效,当设置为正整数时才生效。该属性主要指定在执行节点死亡的情况下锁应该保持多长时间。这只是一个应变的计划,在正常情况下,锁会在任务完成时释放,即使节点死亡,过了设定的该时间值也会被释放,避免死锁的情况发生。但是必须将lockAtMostFor设置为一个比正常执行时间长得多的值,具体的多少,这个就需要根据自身对任务的判断了。如果任务花费的时间与lockAtMostFor时间相比,结果可能是不可预测的(尤其当多个进程将有效地持有锁时)。 |
lockAtMostForString | String | 成功执行任务的节点所能拥有的独占锁的最长时间的字符串表达式。例如:“PT30S”,表示30秒;“PT10M”,表示14分钟。 |
lockAtLeastFor | long | 成功执行任务的节点所能拥有独占所的最短时间,单位是毫秒ms。默认值为:-1L,表示不生效,当设置为正整数时才生效,lockAtLeastFor属性指定应该保留锁的最小时间值,它的主要目的是为了解决多个节点执行时任务执行时间短,节点和节点之间的时间差异问题(例如:A节点的时间为18:00,B节点的时间为18:08,此时的时间差为8分钟,所以这个时候就因为指定lockAtLeastFor>=8M,确保相同的任务在这个时段内只执行一次,不会超过一次)。 |
lockAtLeastForString | String | 成功执行任务的节点所能拥有的独占锁的最短时间的字符串表达式。例如:“PT30S”,表示30秒;“PT10M”,表示14分钟。 |
@EnableSchedulerLock 作用:开启ShedLock的支持
interceptMode | 默认为:PROXY_METHOD |
---|---|
defaultLockAtMostFor | 成功执行任务的节点所能拥有的独占锁的最长时间的字符串表达,例如“PT30S”表示为30秒 |
defaultLockAtLeastFor | 成功执行任务的节点所能拥有的独占锁的最短时间的字符串表达,例如“PT30S”表示为30秒,默认为:“PT0S” |
导mode | 默认为:AdviceMode.PROXY |
proxyTargetClass | 默认为:false |