找回密码
 立即注册
首页 业界区 业界 如何用 ShedLock 让 Spring Boot 的定时任务在多实例环 ...

如何用 ShedLock 让 Spring Boot 的定时任务在多实例环境下只执行一次

姬宜欣 7 天前
之前在Spring Boot教程中我们介绍了如何用 @Scheduled 注解来创建定时任务,Spring 的任务调度用起来确实顺手。可这种实现方式一上多实例(比如多副本部署),同一个定时任务会在每个节点都跑一遍,等于任务会重复执行。
原因很简单:默认情况下,Spring 不会在多个实例之间做调度同步。
这篇文章就聊聊怎么用 ShedLock,让定时任务在多实例环境下“同一时刻只跑一次”。顺便一提,它也能作为 Quartz 的替代。
Maven 依赖

先引入 shedlock-spring 这个依赖:
  1. <dependency>
  2.     <groupId>net.javacrumbs.shedlock</groupId>
  3.     shedlock-spring</artifactId>
  4.     <version>6.3.1</version>
  5. </dependency>
复制代码
最新版本可以去 Maven Central 看。
配置

ShedLock 依赖“共享数据库”,并且要声明一个合适的 LockProvider。它会在库里新建一张表/文档,记录当前的锁。
目前它支持 Mongo、Couchbase、Elasticsearch、Redis、Hazelcast、ZooKeeper、Cassandra,以及任何带 JDBC 驱动的数据库。
示例我们用内存型 H2 数据库,方便演示。
要跑起来,先把 H2 和 JDBC 版的 ShedLock 依赖加上:
  1. <dependency>
  2.     <groupId>net.javacrumbs.shedlock</groupId>
  3.     shedlock-provider-jdbc-template</artifactId>
  4.     <version>6.3.1</version>
  5. </dependency>
  6. <dependency>
  7.      <groupId>com.h2database</groupId>
  8.      h2</artifactId>
  9.      <version>2.1.214</version>
  10. </dependency>
复制代码
然后建一张表,专门存锁:
  1. CREATE TABLE shedlock (
  2.   name VARCHAR(64),
  3.   lock_until TIMESTAMP(3) NULL,
  4.   locked_at TIMESTAMP(3) NULL,
  5.   locked_by VARCHAR(255),
  6.   PRIMARY KEY (name)
  7. )
复制代码
在 Spring Boot 里把数据源写到配置里,这样 DataSource 才能被注入。这里用 application.yml:
  1. spring:
  2.   datasource:
  3.     driverClassName: org.h2.Driver
  4.     url: jdbc:h2:mem:shedlock_DB;INIT=CREATE SCHEMA IF NOT EXISTS shedlock;DB_CLOSE_DELAY=-1;DB_CLOSE_ON_EXIT=FALSE
  5.     username: sa
  6.     password:
复制代码
接着用这个数据源配置下 LockProvider,写法很直观:
  1. @Configuration
  2. public class SchedulerConfiguration {
  3.     @Bean
  4.     public LockProvider lockProvider(DataSource dataSource) {
  5.         return new JdbcTemplateLockProvider(dataSource);
  6.     }
  7. }
复制代码
别忘了再加上两个注解:@EnableScheduling 和 @EnableSchedulerLock:
  1. @SpringBootApplication
  2. @EnableScheduling
  3. @EnableSchedulerLock(defaultLockAtMostFor = "PT30S")
  4. public class SpringBootShedlockApplication {
  5.     public static void main(String[] args) {
  6.         SpringApplication.run(SpringBootShedlockApplication.class, args);
  7.     }
  8. }
复制代码
defaultLockAtMostFor 表示执行节点挂了时,锁最多保留多久。格式用的是 ISO8601 持续时间。
下面的示例会演示怎么在方法上覆盖它。
创建任务

让 ShedLock 接管一个定时任务很简单:方法上同时加 @Scheduled 和 @SchedulerLock:
  1. @Component
  2. class BaeldungTaskScheduler {
  3.     @Scheduled(cron = "0 0/15 * * * ?")
  4.     @SchedulerLock(name = "TaskScheduler_scheduledTask",
  5.       lockAtLeastFor = "PT5M", lockAtMostFor = "PT14M")
  6.     public void scheduledTask() {
  7.         // ...
  8.     }
  9. }
复制代码
先说 @Scheduled:它支持 cron 表达式,上面的表达式表示“每 15 分钟执行一次”。
再说 @SchedulerLock:name 要唯一,一般用 类名_方法名 就够了。我们不希望同一个方法被同时运行,ShedLock 就是靠这个唯一名称来实现的。
我们还加了两个可选参数:

  • lockAtLeastFor 用来保证最少持锁时间,让两次执行之间留出一定间隔。使用 “PT5M” 表示至少 5 分钟。换句话说,这个方法被 ShedLock 控制后,运行频率不会高于每 5 分钟一次。
  • lockAtMostFor 用来指定在执行节点异常(比如宕机)时,锁最多会被保留多久。使用 “PT14M” 表示最多 14 分钟。
正常情况下任务结束会立即释放锁。其实在 @EnableSchedulerLock 里已经有默认值,这里只是展示如何在方法级别做覆盖。
总结

一句话总结:用 ShedLock,可以让 Spring 在多实例部署下也能把定时任务“稳稳只跑一次”。

来源:程序园用户自行投稿发布,如果侵权,请联系站长删除
免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作!

相关推荐

您需要登录后才可以回帖 登录 | 立即注册