SpringBoot集成ShedLock實(shí)現(xiàn)分布式定時(shí)任務(wù)
1.什么是ShedLock?
ShedLock 是一個(gè) Java 庫,通常用于分布式系統(tǒng)中,確保定時(shí)任務(wù)(Scheduled Tasks)在集群環(huán)境下只被某一個(gè)實(shí)例執(zhí)行一次。它通過在共享資源(例如數(shù)據(jù)庫或分布式緩存)中添加鎖的方式,避免多個(gè)實(shí)例同時(shí)執(zhí)行相同的任務(wù)
ShedLock 的工作原理
- 分布式鎖:
- 在任務(wù)開始時(shí),ShedLock 會嘗試在數(shù)據(jù)庫(或其他存儲)中創(chuàng)建一個(gè)鎖。
- 如果某個(gè)實(shí)例成功獲取鎖,則只有它能執(zhí)行該任務(wù)。
- 鎖的生命周期:
- 鎖會有一個(gè)明確的過期時(shí)間(鎖的持有時(shí)間,
lockAtLeastFor和lockAtMostFor參數(shù)配置)。 - 鎖過期后,即使任務(wù)異常終止,其他實(shí)例也可以重新獲取鎖。
- 鎖會有一個(gè)明確的過期時(shí)間(鎖的持有時(shí)間,
- 支持的存儲:
- 支持多種鎖存儲,包括數(shù)據(jù)庫(如 MySQL、PostgreSQL)、Redis、MongoDB 等。
應(yīng)用場景
1. 分布式定時(shí)任務(wù)控制
- 在分布式環(huán)境中,多個(gè)實(shí)例會同時(shí)調(diào)度定時(shí)任務(wù)。如果沒有控制,可能導(dǎo)致任務(wù)重復(fù)執(zhí)行。ShedLock 確保只有一個(gè)實(shí)例能運(yùn)行任務(wù)。
- 例如:
- 生成日報(bào)表的定時(shí)任務(wù)。
- 清理過期數(shù)據(jù)的批處理任務(wù)。
2. 避免重復(fù)任務(wù)執(zhí)行
- 即使在單實(shí)例環(huán)境中,也可以使用 ShedLock 避免因意外重啟或配置錯(cuò)誤導(dǎo)致同一任務(wù)被多次觸發(fā)。
3. 事件驅(qū)動任務(wù)的冪等性
- 當(dāng)某些任務(wù)需要對同一事件觸發(fā)處理時(shí),使用 ShedLock 可以確保一個(gè)事件只被處理一次。
4. 任務(wù)重試機(jī)制
- 如果任務(wù)需要重試(例如在發(fā)生錯(cuò)誤時(shí)),ShedLock 能避免多個(gè)實(shí)例同時(shí)重試相同任務(wù)。
2.代碼工程
實(shí)驗(yàn)?zāi)康?/h3>
使用ShedLock 來確保在分布式環(huán)境中只有一個(gè)實(shí)例能運(yùn)行任務(wù)
使用ShedLock 來確保在分布式環(huán)境中只有一個(gè)實(shí)例能運(yùn)行任務(wù)
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 * * * ?")- 定義定時(shí)任務(wù)的調(diào)度時(shí)間。
- 表達(dá)式含義:任務(wù)每分鐘的整點(diǎn)觸發(fā),例如 12:00、12:01、12:02。
- 注意:多個(gè)實(shí)例(分布式環(huán)境)都會在同一時(shí)間調(diào)度到此任務(wù),但通過 ShedLock 確保只有一個(gè)實(shí)例能真正執(zhí)行。
@SchedulerLock- 使用 ShedLock 來管理分布式鎖。
name = "scheduledTaskName":- 定義鎖的唯一標(biāo)識。共享存儲(如數(shù)據(jù)庫或 Redis)中會記錄此鎖的狀態(tài)。
lockAtMostFor = "PT10M":- 鎖的最長持有時(shí)間為 10分鐘。
- 如果任務(wù)運(yùn)行超出 10 分鐘,即使沒有主動釋放鎖,也會自動過期,其他實(shí)例可以繼續(xù)獲取鎖。
lockAtLeastFor = "PT1M":- 鎖的最短持有時(shí)間為 1分鐘。
- 即使任務(wù)提前完成,鎖仍會持有至少 1 分鐘,防止其他實(shí)例快速重復(fù)執(zhí)行任務(wù)。
- 任務(wù)邏輯
System.out.println("Executing scheduled task");是任務(wù)的業(yè)務(wù)邏輯。- 此邏輯只會在獲得鎖的實(shí)例上執(zhí)行。
配置文件
node1節(jié)點(diǎn)
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節(jié)點(diǎn)
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
以上只是一些關(guān)鍵代碼。
3.測試
啟動node1節(jié)點(diǎn)
java -jar myapp.jar --spring.profiles.active=node1
啟動node2節(jié)點(diǎn)
java -jar myapp.jar --spring.profiles.active=node2
通過控制臺觀察日志,可以發(fā)現(xiàn),2個(gè)實(shí)例交替獲取到鎖執(zhí)行,而不是同一時(shí)刻一起執(zhí)行
4.注意事項(xiàng)
- 任務(wù)時(shí)間控制: 確保任務(wù)的實(shí)際執(zhí)行時(shí)間小于
lockAtMostFor,否則任務(wù)可能被其他實(shí)例重復(fù)執(zhí)行。 - 冪等性: 任務(wù)邏輯應(yīng)盡量設(shè)計(jì)為冪等的(重復(fù)執(zhí)行不會產(chǎn)生副作用),以應(yīng)對鎖機(jī)制的潛在異常情況。
- 存儲配置: 確保使用高可用的鎖存儲(如數(shù)據(jù)庫或 Redis),否則鎖機(jī)制可能失效。
以上就是SpringBoot集成ShedLock實(shí)現(xiàn)分布式定時(shí)任務(wù)的詳細(xì)內(nèi)容,更多關(guān)于SpringBoot ShedLock定時(shí)任務(wù)的資料請關(guān)注腳本之家其它相關(guān)文章!
相關(guān)文章
Java中ShardingSphere分庫分表實(shí)戰(zhàn)
我們做項(xiàng)目的時(shí)候,數(shù)據(jù)量比較大,單表千萬級別的,需要分庫分表,本文主要介紹了Java中ShardingSphere分庫分表實(shí)戰(zhàn),感興趣的可以了解一下2021-09-09
Spring中的FactoryBean與ObjectFactory詳解
這篇文章主要介紹了Spring中的FactoryBean與ObjectFactory詳解,FactoryBean是一種特殊的bean,本身又是個(gè)工廠,實(shí)現(xiàn)了FactoryBean的bean會被注冊到容器中,需要的朋友可以參考下2023-12-12
Jmeter參數(shù)化實(shí)現(xiàn)原理及過程解析
這篇文章主要介紹了Jmeter參數(shù)化實(shí)現(xiàn)原理及過程解析,文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友可以參考下2020-07-07
Maven打包編譯出現(xiàn)Fileencodinghasnotbeenset問題的解決方案
在使用Maven進(jìn)行項(xiàng)目構(gòu)建時(shí),有時(shí)會遇到??File encoding has not been set??的錯(cuò)誤提示,這個(gè)問題通常出現(xiàn)在Maven嘗試編譯Java源文件時(shí),本文將詳細(xì)介紹該問題的原因、解決方法以及如何避免類似問題再次發(fā)生,需要的朋友可以參考下2025-11-11

