开发者

SpringBoot集成ShedLock实现分布式定时任务

目录
  • 1.什么是ShedLock?
    • ShedLock 的工作原理
    • 应用场景
    • 1. 分布式定时任务控制
    • 2. 避免重复任务执行
    • 3. 事件驱动任务的幂等性
    • 4. 任务重试机制
  • 2.代码工程
    • 实验目的
    • pom.XML
    • config
    • cron
    • 配置文件
  • 3.测试
    • 4.注意事项

      1.什么是ShedLock?

      ShedLock 是一个 Java 库,通常用于分布式系统中,确保定时任务(Scheduled Tasks)在集群环境下只被某一个实例执行一次。它通过在共享资源(例如数据库或分布式缓存)中添加锁的方式,避免多个实例同时执行相同的任务

      ShedLock 的工作原理

      • 分布式锁
        • 在任务开始时,ShedLock 会尝试在数据库(或其他存储)中创建一个锁。
        • 如果某个实例成功获取锁,则只有它能执行该任务。
      • 锁的生命周期
        • 锁会有一个明确的过期时间(锁的持有时间,lockAtLeastForlockAtMostFor 参数配置)。
        • 锁过期后,即使任务异常终止,其他实例也可以重新获取锁。
      • 支持的存储
        • 支持多种锁存储,包括数据库(如 mysql、PostgreSQL)、Redis、MongoDB 等。

      应用场景

      1. 分布式定时任务控制

      • 在分布式环境中,多个实例会同时调度定时任务。如果没有控制,可能导致任务重复执行。ShedLock 确保只有一个实例能运行任务。
      • 例如:
        • 生成日报表的定时任务。
        • 清理过期数据的批处理任务。

      2. 避免重复任务执行

      • 即使在单实例环境中,也可以使用 ShedLock 避免因意外重启或配置错误导致同一任务被多次触发。

      3. 事件驱动任务的幂等性

      • 当某些任务需要对同一事件触发处理时,使用 ShedLock 可以确保一个事件只被处理一次。

      4. 任务重试机制

      • 如果任务需要重试(例如在发生错误时),ShedLock 能避免多个实例同时重试相同任务。

      2.代码工程

      实验目的

      使用ShedLock 来确保在分布式环境中只有一个实例能运行任务

      pom.xml

      <?xml version="1.0" encoding="UTF-8"?>
      <project xmlns="http://maven.apache.org/POM/4.0.0"
               xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
               xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
          <parent>
              <groupId>org.springframework.boot</groupId>
              <artifactId>spring-boot-starter-parent</artifactId>
              <version>3.2.1</version>
          </parent>
      
          <modelVersion>4.0.0</modelVersion>
      
          <artifactId>shedlock</artifactId>
      
          <properties>
              <maven.compiler.source>17</maven.compiler.source>
              <maven.compiler.target>17</maven.compiler.target>
          </properties>
          <dependencies>
              <dependency>
                  <groupId>org.springframework.boot</groupId>
                  <artifactId>spring-boot-starter-web</artifactId>
              </dependency>
      
              <dependency>
                  <groupId>org.springframework.boot</groupId>
                  <artifactId>spring-boot-autoconfigure</artifactId>
              </dependency>
              <dependency>
                  <groupId>org.springframework.boot</groupId>
                  <artifactId>spring-boot-starter-test</artifactId>
                  <scope>test</scope>
              </dependency>
              <dependency>
                  <groupId>net.javacrumbs.shedlock</groupId>
                  <artifactId>shedlock-spring</artifactId>
                  <version>5.5.0</version>
              </dependency>
              <dependency>
                  <groupId>net.javacrumbs.shedlock</groupId>
                  <artifactId>shedlock-provider-jdbc-template</artifactId>
                  <version>5.5.0</version>
              </dependency>
              <dependency>
                  <groupId>org.springframework.boot</groupId>android;
                  <artifactId>spring-boot-starter-data-jpa</artifactId>
              </dependency>
              <dependency>
                  <groupId>mysql</groupId>
                  <artifactId>mysql-connector-java</artifactId>
                  <version>8.0.33</version>
              </dependency>
          </dependencies>
      </project>
      

      config

      package com.demo.config;
      
      import net.javacrumbs.shedlock.core.LockProvider;
      import net.javacrumbs.shedlock.provider.jdbctemplate.JdbcTemplateLockProvider;
      import org.springframework.context.annotation.Bean;
      import orgphp.springframework.context.annotation.Configuration;
      import org.springframework.jdbc.core.JdbcTemplate;
      
      import javax.sql.DataSource;
      
      @Configuration
      public class ShedLockConfig {
      
          @Bean
          public LockProvider lockProvider(DataSource dataSource) {
              return new JdbcTemplateLockProvider(
                  JdbcTemplateLockProvider.Configuration.builder()
                      .withJdbcTemplate(new JdbcTemplate(dataSource))
                      .usingDbTime() // Works with PostgreSQL, MySQL, mariadb, MS SQL, oracle, HSQL, H2, DB2, and others
                      .build()
              );
          }
      }
      

      cron

      package com.demo.cron;
      
      import net.javacrumbs.shedlock.spring.annotation.EnableSchedulerLock;
      import net.javacrumbs.shedlock.spring.annotation.SchedulerLock;
      import org.springframework.context.annotation.Configuration;
      import org.springframework.scheduling.annotation.EnableScphpheduling;
      import org.springframework.scheduling.annotation.Scheduled;
      
      @Configuration
      @EnableScheduling
      @EnableSchedulerLock(defaultLockAtMostFor = "PT30S")
      public class SchedulerConfig {
      
          @Scheduled(cron = "0 0/1 * * * ?")
          @SchedulerLock(name = "scheduledTaskName", lockAtMostFor = "PT10M", lockAtLeastFor = "PT1M")
          public void scheduledTask() {
              //  some logic code
              System.out.println("Executing scheduled task");
          }
      
      }
      
      • @Scheduled(cron = "0 0/1 * * * ?")
        • 定义定时任务的调度时间。
        • 表达式含义:任务每分钟的整点触发,例如 12:00、12:01、12:02。
        • 注意:多个实例(分布式环境)都会在同一时间调度到此任务,但通过 ShedLock 确保只有一个实例能真正执行。
      • @SchedulerLock
        • 使用 ShedLock 来管理分布python式锁。
        • name = "scheduledTaskName":
          • 定义锁的唯一标识。共享存储(如数据库或 Redis)中会记录此锁的状态。
        • lockAtMostFor = "PT10M":
          • 锁的最长持有时间为 10分钟
          • 如果任务运行超出 10 分钟,即使没有主动释放锁,也会自动过期,其他实例可以继续获取锁。
        • lockAtLeastFor = "PT1M":
          • 锁的最短持有时间为 1分钟
          • 即使任务提前完成,锁仍会持有至少 1 分钟,防止其他实例快速重复执行任务。
      • 任务逻辑
        • System.out.println("Executing scheduled task"); 是任务的业务逻辑。
        • 此逻辑只会在获得锁的实例上执行。

      配置文件

      node1节点

      spring.datasource.url=jdbc:mysql://127.0.0.1:3306/demo?useUnicode=true&characterEncoding=utf-8&useSSL=false
      spring.datasource.username=root
      spring.datasource.password=123456
      spring.datasource.driver-class-name=com.mysql.cj.jdbc.Driver
      server.port=8081
      

      node2节点

      spring.datasource.url=jdbc:mysql://127.0.0.1:3306/demo?useUnicode=true&characterEncoding=utf-8&useSSL=false
      spring.datasource.username=root
      spring.datasource.password=123456
      spring.datasource.driver-class-name=com.mysql.cj.jdbc.Driver
      server.port=8082
      

      以上只是一些关键代码。

      3.测试

      启动node1节点

      java -jar myapp.jar --spring.profiles.active=node1
      

      启动node2节点

      java -jar myapp.jar --spring.profiles.active=node2
      

      通过控制台观察日志,可以发现,2个实例交替获取到锁执行,而不是同一时刻一起执行

      4.注意事项

      • 任务时间控制: 确保任务的实际执行时间小于 lockAtMostFor,否则任务可能被其他实例重复执行。
      • 幂等性: 任务逻辑应尽量设计为幂等的(重复执行不会产生副作用),以应对锁机制的python潜在异常情况。
      • 存储配置: 确保使用高可用的锁存储(如数据库或 Redis),否则锁机制可能失效。

      以上就是SpringBoot集成ShedLock实现分布式定时任务的详细内容,更多关于SpringBoot ShedLock定时任务的资料请关注编程客栈(www.devze.com)其它相关文章!

      0

      上一篇:

      下一篇:

      精彩评论

      暂无评论...
      验证码 换一张
      取 消

      最新开发

      开发排行榜