1.什么是shedlock?
shedlock 是一个 java 库,通常用于分布式系统中,确保定时任务(scheduled tasks)在集群环境下只被某一个实例执行一次。它通过在共享资源(例如数据库或分布式缓存)中添加锁的方式,避免多个实例同时执行相同的任务
shedlock 的工作原理
- 分布式锁:
- 在任务开始时,shedlock 会尝试在数据库(或其他存储)中创建一个锁。
- 如果某个实例成功获取锁,则只有它能执行该任务。
- 锁的生命周期:
- 锁会有一个明确的过期时间(锁的持有时间,
lockatleastfor和lockatmostfor参数配置)。 - 锁过期后,即使任务异常终止,其他实例也可以重新获取锁。
- 锁会有一个明确的过期时间(锁的持有时间,
- 支持的存储:
- 支持多种锁存储,包括数据库(如 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定时任务的资料请关注代码网其它相关文章!
发表评论