@Schedule 通过 ShedLock 实现分布式定时任务
默认的 @Schedule
注解实现的定时任务是单个应用的,当开启了多个实例,每个实例中都会执行一次。
此时可通过添加 shedlock-spring 包来支持这种分布式场景。
1. 在 pom.xml 中添加依赖
这里使用的版本是 4.20.1 。
xml
<!-- ShedLock -->
<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>
</dependency>
2. 添加配置文件
@EnableScheduling
注解用于启用定时任务。@EnableSchedulerLock
注解用于启用 ShedLock 。
这里使用数据库来实现任务的锁竞争,所以定义了一个 JdbcTemplateLockProvider
类型的 Bean 。
更多的锁实现方式请参考 这篇文章 。
java
import net.javacrumbs.shedlock.core.LockProvider;
import net.javacrumbs.shedlock.provider.jdbctemplate.JdbcTemplateLockProvider;
import net.javacrumbs.shedlock.spring.annotation.EnableSchedulerLock;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.scheduling.annotation.EnableScheduling;
import javax.sql.DataSource;
/**
* @author jiajia
*/
@Configuration
@EnableScheduling
@EnableSchedulerLock(defaultLockAtMostFor = "PT30S")
public class ShedLockConfiguration {
@Bean
public LockProvider lockProvider(DataSource dataSource) {
return new JdbcTemplateLockProvider(dataSource);
}
}
3. 创建数据库表
使用数据库锁时需要创建对应的表。
sql
CREATE TABLE shedlock(
name VARCHAR(64),
lock_until TIMESTAMP(3) NULL,
locked_at TIMESTAMP(3) NULL,
locked_by VARCHAR(255),
PRIMARY KEY (name)
)
4. 创建定时任务
注解的属性值使用了 SpEL ,以方便在配置中心中修改。
lockAtMostFor:锁最长保留时间(防止某个应用实例宕机导致锁被长时间占用)
lockAtLeastFor:锁最短保留时间(防止多个应用实例间有时间差)
java
@Scheduled(cron = "${task.sample.cron:0 0/10 * * * ?}")
@SchedulerLock(name = "sample-task",
lockAtMostFor = "${task.sample.lock-at-most-for:60s}",
lockAtLeastFor = "${task.sample.lock-at-least-for:10s}")
public void sampleTask() {
// do something
}