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

 更新时间:2024年11月20日 08:38:56   作者:HBLOG  
ShedLock 是一个 Java 库,通常用于分布式系统中,确保定时任务(Scheduled Tasks)在集群环境下只被某一个实例执行一次,它通过在共享资源中添加锁的方式,本文给大家介绍了SpringBoot集成ShedLock实现分布式定时任务,需要的朋友可以参考下

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>
            <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 org.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.EnableScheduling;
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 来管理分布式锁。
    • 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,否则任务可能被其他实例重复执行。
  • 幂等性: 任务逻辑应尽量设计为幂等的(重复执行不会产生副作用),以应对锁机制的潜在异常情况。
  • 存储配置: 确保使用高可用的锁存储(如数据库或 Redis),否则锁机制可能失效。

以上就是SpringBoot集成ShedLock实现分布式定时任务的详细内容,更多关于SpringBoot ShedLock定时任务的资料请关注脚本之家其它相关文章!

相关文章

  • Spring Boot中使用 Spring Security 构建权限系统的示例代码

    Spring Boot中使用 Spring Security 构建权限系统的示例代码

    本篇文章主要介绍了Spring Boot中使用 Spring Security 构建权限系统的示例代码,具有一定的参考价值,有兴趣的可以了解一下
    2017-08-08
  • 快速上手Mybatis-plus结构构建过程

    快速上手Mybatis-plus结构构建过程

    这篇文章主要介绍了快速上手Mybatis-plus结构构建过程,具有很好的参考价值,希望对大家有所帮助,如有错误或未考虑完全的地方,望不吝赐教
    2024-07-07
  • Java 字符数组转字符串的常用方法

    Java 字符数组转字符串的常用方法

    文章总结了在Java中将字符数组转换为字符串的几种常用方法,包括使用String构造函数、String.valueOf()方法、StringBuilder以及Arrays.toString()方法,每种方法都有其适用的场景和性能特点,感兴趣的朋友跟随小编一起看看吧
    2025-01-01
  • SpringBoot中静态访问配置属性的解决方案对比

    SpringBoot中静态访问配置属性的解决方案对比

    在SpringBoot开发中,静态访问配置信息是一个常见需求,尤其是在工具类中直接获取配置值,下面我们就来看看几个常用的方法,大家可以根据需要选择
    2025-03-03
  • MyBatis-Plus 分页插件配置的两种方式实现

    MyBatis-Plus 分页插件配置的两种方式实现

    本文主要介绍了MyBatis-Plus 分页插件配置的两种方式实现,包括使用PaginationInterceptor和MybatisPlusInterceptor两种方式,具有一定的参考价值,感兴趣的可以了解一下
    2025-03-03
  • 使用Maven中的scope总结

    使用Maven中的scope总结

    这篇文章主要介绍了使用Maven中的scope总结,具有很好的参考价值,希望对大家有所帮助。如有错误或未考虑完全的地方,望不吝赐教
    2022-06-06
  • easyexcel读取excel合并单元格数据的操作代码

    easyexcel读取excel合并单元格数据的操作代码

    这篇文章主要介绍了easyexcel读取excel合并单元格数据的操作代码,本文通过实例代码给大家介绍的非常详细,对大家的学习或工作具有一定的参考借鉴价值,需要的朋友可以参考下
    2023-05-05
  • Spring的嵌套事务(Propagation.NESTED)到底是个啥案例代码讲解

    Spring的嵌套事务(Propagation.NESTED)到底是个啥案例代码讲解

    SavePoint是数据库事务中的一个概念, 可以将整个事务切割为不同的小事务,可以选择将状态回滚到某个小事务发生时的样子,本文通过案例代码讲解Spring的嵌套事务(Propagation.NESTED)到底是个啥,感兴趣的朋友跟随小编一起看看吧
    2023-01-01
  • java面向对象设计原则之接口隔离原则示例详解

    java面向对象设计原则之接口隔离原则示例详解

    这篇文章主要为大家介绍了java面向对象设计原则之接口隔离原则的示例详解,有需要的朋友可以借鉴参考下希望能够有所帮助,祝大家多多进步早日升职加薪
    2021-10-10
  • JDK8 中Arrays.sort() 排序方法详解

    JDK8 中Arrays.sort() 排序方法详解

    这篇文章主要介绍了JDK8 中Arrays.sort() 排序方法解读,本文先行介绍Arrays.sort()中影响排序方式的几个因素,影响因素主要为数组类型、数组大小,结合阈值对排序方式进行选择,需要的朋友可以参考下
    2023-05-05

最新评论