Spring Boot 整合 ShedLock 處理定時(shí)任務(wù)重復(fù)執(zhí)行的問題小結(jié)
前言
在分布式系統(tǒng)中,定時(shí)任務(wù)的執(zhí)行往往需要考慮到多個(gè)實(shí)例的并發(fā)執(zhí)行問題。假設(shè)一個(gè)定時(shí)任務(wù)會(huì)在多個(gè)節(jié)點(diǎn)上并發(fā)執(zhí)行,可能導(dǎo)致重復(fù)執(zhí)行,甚至引發(fā)數(shù)據(jù)異常或系統(tǒng)不一致問題。為了解決這一問題,ShedLock
是一個(gè)簡單而有效的解決方案,它可以確保在分布式環(huán)境中,只有一個(gè)節(jié)點(diǎn)在某一時(shí)刻執(zhí)行指定的定時(shí)任務(wù)。
什么是 ShedLock?
ShedLock
是一個(gè)輕量級(jí)的 Java 庫,用于解決分布式系統(tǒng)中定時(shí)任務(wù)的重復(fù)執(zhí)行問題。它的核心思想是在數(shù)據(jù)庫中加鎖,確保在分布式環(huán)境下,只有一個(gè)節(jié)點(diǎn)能夠在指定時(shí)間執(zhí)行某個(gè)任務(wù)。ShedLock
可以與 Spring Scheduler
、Quartz
等定時(shí)任務(wù)框架結(jié)合使用。
github開源地址:https://github.com/lukas-krecan/ShedLock 目前擁有 3.7K Star,小伙伴們也可以針對(duì)文檔更系統(tǒng)性的學(xué)習(xí)如何應(yīng)用ShedLock。
ShedLock 的工作原理:
? 定時(shí)任務(wù)執(zhí)行時(shí),ShedLock 會(huì)嘗試在數(shù)據(jù)庫中為該任務(wù)獲取鎖。
? 如果當(dāng)前節(jié)點(diǎn)成功獲取到鎖,則執(zhí)行定時(shí)任務(wù)。
? 如果當(dāng)前節(jié)點(diǎn)未能獲取到鎖(其他節(jié)點(diǎn)已獲取鎖),則該任務(wù)跳過執(zhí)行,等待下次調(diào)度。
定時(shí)任務(wù)重復(fù)執(zhí)行的問題
在分布式應(yīng)用中,定時(shí)任務(wù)往往通過多個(gè)實(shí)例運(yùn)行,每個(gè)實(shí)例都會(huì)按照自己的調(diào)度計(jì)劃執(zhí)行任務(wù)。例如,假設(shè)有一個(gè)定時(shí)任務(wù)負(fù)責(zé)同步某個(gè)外部系統(tǒng)的數(shù)據(jù)。如果該任務(wù)在多個(gè)實(shí)例上同時(shí)執(zhí)行,就會(huì)導(dǎo)致重復(fù)的數(shù)據(jù)同步,進(jìn)而造成數(shù)據(jù)不一致、重復(fù)處理等問題。
典型場景
我們來模擬一個(gè)場景,假設(shè)在一個(gè)電商系統(tǒng)中,定時(shí)任務(wù)負(fù)責(zé)將庫存數(shù)據(jù)同步到第三方庫存管理系統(tǒng)。如果該任務(wù)在多個(gè)節(jié)點(diǎn)上同時(shí)執(zhí)行,可能會(huì)導(dǎo)致:
1、重復(fù)的庫存更新請(qǐng)求。
2、數(shù)據(jù)不同步或數(shù)據(jù)丟失。
3、系統(tǒng)負(fù)載過高,造成性能瓶頸。
使用 ShedLock 解決定時(shí)任務(wù)重復(fù)執(zhí)行問題
接下來,博主將通過 Spring Boot 和 ShedLock 來演示如何解決定時(shí)任務(wù)的重復(fù)執(zhí)行問題。希望能給大家一個(gè)參考!
? 添加依賴
首先確保我們的的 pom.xml 中包含必要的依賴。需要添加 Spring Boot Starter、Spring Scheduling 和 ShedLock 相關(guān)的依賴。
其中博主使用的是Mysql作為ShedLock初始化數(shù)據(jù)庫,引入shedlock-provider-jdbc-template
,大家可以根據(jù)自己的實(shí)際情況,根據(jù)作者的文檔選擇,如下圖:
<dependencies> <!-- Spring Boot Starter Web --> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> </dependency> <!-- Spring Boot Scheduler --> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-scheduled</artifactId> </dependency> <!-- ShedLock Core --> <dependency> <groupId>net.javacrumbs.shedlock</groupId> <artifactId>shedlock-spring</artifactId> <version>6.2.0</version> </dependency> <!-- ShedLock JDBC --> <dependency> <groupId>net.javacrumbs.shedlock</groupId> <artifactId>shedlock-provider-jdbc-template</artifactId> <version>6.2.0</version> </dependency> <!-- MySQL JDBC Driver --> <dependency> <groupId>mysql</groupId> <artifactId>mysql-connector-java</artifactId> </dependency> </dependencies>
對(duì)應(yīng)版本兼容性依賴:
ShedLock版本 | 最低JVM版本 | Tested with |
---|---|---|
6.x.x | 17 | Spring 6.2, 6.1 Spring Boot 3.4, 3.3 Micronaut 4 |
5.x.x | 17 | Spring 6.1, 6.0 Spring Boot 3.4, 3.3, 3.2 Micronaut 3, 4 |
4.x.x | 8 | Spring 6.0, 5.3 Spring Boot 3.0, 2.7, 2.6 |
3.x.x | 8 | Spring 5.2, 5.1 Spring Boot 2.2, 2.1 |
2.x.x | 8 | Spring 5.1, 5.0 Spring Boot 2.1 |
1.x.x | 8 | Spring 5.0 Spring Boot 2.0 |
? 配置數(shù)據(jù)庫
ShedLock 需要在數(shù)據(jù)庫中存儲(chǔ)鎖的信息。這里博主使用 MySQL 來存儲(chǔ)鎖。首先,創(chuàng)建一個(gè)名為 shedlock 的表來存儲(chǔ)鎖數(shù)據(jù)。
CREATE TABLE shedlock ( name VARCHAR(64) PRIMARY KEY, lock_until TIMESTAMP(3) NOT NULL, locked_at TIMESTAMP(3) NOT NULL, locked_by VARCHAR(255) NOT NULL );
該表的字段意義如下:
- name: 鎖的名稱,用于區(qū)分不同的任務(wù)。
- lock_until: 鎖的過期時(shí)間,任務(wù)完成后應(yīng)釋放鎖。
- locked_at: 鎖的創(chuàng)建時(shí)間。
- locked_by: 鎖被哪個(gè)節(jié)點(diǎn)持有。
? 配置 ShedLock
接下來,我們?cè)?Spring Boot
配置類中進(jìn)行 ShedLock
的配置。以下是一個(gè)基本的配置類:
import net.javacrumbs.shedlock.spring.annotation.EnableSchedulerLock; import org.springframework.context.annotation.Configuration; import org.springframework.scheduling.annotation.EnableScheduling; @Configuration @EnableScheduling @EnableSchedulerLock(defaultLockAtMostFor = "30m") //默認(rèn)鎖最大過期時(shí)間為 30 秒 public class ShedLockConfig { }
注解說明:
@EnableSchedulerLock: 啟用 ShedLock
功能,defaultLockAtMostFor
參數(shù)指定鎖的最大過期時(shí)間。如果任務(wù)執(zhí)行超過此時(shí)間,鎖將會(huì)被釋放。
@EnableScheduling: 啟用 Spring
的調(diào)度功能。
? 創(chuàng)建定時(shí)任務(wù)
接下來我們創(chuàng)建一個(gè)定時(shí)任務(wù),并為其添加 ShedLock
鎖,確保在分布式環(huán)境中只有一個(gè)實(shí)例會(huì)執(zhí)行該任務(wù)。
package com.example; import net.javacrumbs.shedlock.spring.annotation.SchedulerLock; import org.springframework.scheduling.annotation.Scheduled; import org.springframework.stereotype.Service; @Service public class DataSyncService { /** * 使用 @SchedulerLock 注解來確保該任務(wù)只會(huì)在一個(gè)節(jié)點(diǎn)上執(zhí)行 */ @Scheduled(fixedRate = 5000) // 每 5 秒執(zhí)行一次 @SchedulerLock(name = "syncInventoryTask", lockAtMostFor = "5m", lockAtLeastFor = "5m") public void syncInventory() { System.out.println("同步庫存數(shù)據(jù)..."); // 模擬庫存同步操作 try { Thread.sleep(3000); // 模擬執(zhí)行耗時(shí) 3 秒 } catch (InterruptedException e) { Thread.currentThread().interrupt(); } System.out.println("庫存同步完成!"); } }
上述代碼中,我們?cè)谠?syncInventory 方法上,我們使用了 @SchedulerLock 注解,指定鎖的名稱 syncInventoryTask,并設(shè)置鎖的最大持續(xù)時(shí)間為 5 秒。
- name: 鎖的名稱,確保每個(gè)定時(shí)任務(wù)有唯一的名稱。
- lockAtMostFor: 鎖的最大過期時(shí)間,防止任務(wù)因?yàn)楫惓6鵁o法釋放鎖。
- lockAtLeastFor: 鎖的最小持續(xù)時(shí)間,確保鎖至少保持一定時(shí)間。
? 配置數(shù)據(jù)庫連接
最后 application.properties 或 application.yml 配置我們的數(shù)據(jù)庫連接信息
spring.datasource.url=jdbc:mysql://localhost:3306/your_database spring.datasource.username=root spring.datasource.password=your_password spring.datasource.driver-class-name=com.mysql.cj.jdbc.Driver spring.jpa.hibernate.ddl-auto=update
? 啟動(dòng)應(yīng)用測(cè)試
最后我們啟動(dòng) Spring Boot
應(yīng)用,查看控制臺(tái)輸出。你將會(huì)看到 syncInventory
任務(wù)每 5 秒鐘執(zhí)行一次,但只有一個(gè)節(jié)點(diǎn)能夠在任何時(shí)刻成功執(zhí)行該任務(wù),避免了多節(jié)點(diǎn)并發(fā)執(zhí)行造成的問題。
總結(jié)
相信小伙伴們通過本文的示例,我們演示了如何使用 ShedLock
解決分布式系統(tǒng)中定時(shí)任務(wù)重復(fù)執(zhí)行的問題。ShedLock
提供了一種簡單有效的方式來確保在多實(shí)例部署的環(huán)境中,定時(shí)任務(wù)不會(huì)被重復(fù)執(zhí)行,避免了數(shù)據(jù)不一致等問題。
- 應(yīng)用場景: 分布式定時(shí)任務(wù)、任務(wù)調(diào)度、數(shù)據(jù)同步等場景。
- 優(yōu)勢(shì): 簡單易用、無需復(fù)雜配置、適配多種存儲(chǔ)方式(如 JDBC、MongoDB、Redis等)
到此這篇關(guān)于Spring Boot 整合 ShedLock 處理定時(shí)任務(wù)重復(fù)執(zhí)行的問題的文章就介紹到這了,更多相關(guān)Spring Boot ShedLock 定時(shí)任務(wù)重復(fù)執(zhí)行內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
Java數(shù)據(jù)結(jié)構(gòu)之HashMap和HashSet
這篇文章主要介紹了HashMap和HashSet,什么是哈希表以及HashMap的部分源碼解讀,想了解更多的小伙伴,可以參考閱讀本文2023-03-03利用Java實(shí)現(xiàn)網(wǎng)站聚合工具
互聯(lián)網(wǎng)上有數(shù)以萬億計(jì)的網(wǎng)站,每個(gè)網(wǎng)站大都具有一定的功能。搜索引擎雖然對(duì)互聯(lián)網(wǎng)上的部分網(wǎng)站建立了索引,但是其作為一個(gè)大而全的搜索系統(tǒng),無法很好的定位到一些特殊的需求。因此本文將介紹一個(gè)用java實(shí)現(xiàn)的網(wǎng)站數(shù)據(jù)聚合工具,需要的可以參考一下2022-01-01SpringBoot JPA實(shí)現(xiàn)查詢多值
這篇文章主要為大家詳細(xì)介紹了SpringBoot JPA實(shí)現(xiàn)查詢多值,文中示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2018-08-08java實(shí)現(xiàn)用戶簽到BitMap功能實(shí)現(xiàn)demo
這篇文章主要為大家介紹了java實(shí)現(xiàn)用戶簽到BitMap功能實(shí)現(xiàn)demo,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪2023-11-11如何實(shí)用Java實(shí)現(xiàn)合并、拆分PDF文檔
這篇文章主要介紹了如何實(shí)用Java實(shí)現(xiàn)合并、拆分PDF文檔,處理PDF文檔時(shí),這樣的好處是對(duì)文檔的存儲(chǔ)、管理很方便。下面將通過Java程序代碼介紹具體的PDF合并、拆分的方法,需要的朋友可以參考下2019-07-07Java數(shù)據(jù)結(jié)構(gòu)學(xué)習(xí)之樹
這篇文章主要介紹了Java數(shù)據(jù)結(jié)構(gòu)學(xué)習(xí)之樹,文中有非常詳細(xì)的代碼示例,對(duì)正在學(xué)習(xí)java數(shù)據(jù)結(jié)構(gòu)的小伙伴們有非常好的幫助,需要的朋友可以參考下2021-05-05