Spring Boot 整合 ShedLock 處理定時任務重復執(zhí)行的問題小結
前言
在分布式系統(tǒng)中,定時任務的執(zhí)行往往需要考慮到多個實例的并發(fā)執(zhí)行問題。假設一個定時任務會在多個節(jié)點上并發(fā)執(zhí)行,可能導致重復執(zhí)行,甚至引發(fā)數據異常或系統(tǒng)不一致問題。為了解決這一問題,ShedLock
是一個簡單而有效的解決方案,它可以確保在分布式環(huán)境中,只有一個節(jié)點在某一時刻執(zhí)行指定的定時任務。
什么是 ShedLock?
ShedLock
是一個輕量級的 Java 庫,用于解決分布式系統(tǒng)中定時任務的重復執(zhí)行問題。它的核心思想是在數據庫中加鎖,確保在分布式環(huán)境下,只有一個節(jié)點能夠在指定時間執(zhí)行某個任務。ShedLock
可以與 Spring Scheduler
、Quartz
等定時任務框架結合使用。
github開源地址:https://github.com/lukas-krecan/ShedLock 目前擁有 3.7K Star,小伙伴們也可以針對文檔更系統(tǒng)性的學習如何應用ShedLock。
ShedLock 的工作原理:
? 定時任務執(zhí)行時,ShedLock 會嘗試在數據庫中為該任務獲取鎖。
? 如果當前節(jié)點成功獲取到鎖,則執(zhí)行定時任務。
? 如果當前節(jié)點未能獲取到鎖(其他節(jié)點已獲取鎖),則該任務跳過執(zhí)行,等待下次調度。
定時任務重復執(zhí)行的問題
在分布式應用中,定時任務往往通過多個實例運行,每個實例都會按照自己的調度計劃執(zhí)行任務。例如,假設有一個定時任務負責同步某個外部系統(tǒng)的數據。如果該任務在多個實例上同時執(zhí)行,就會導致重復的數據同步,進而造成數據不一致、重復處理等問題。
典型場景
我們來模擬一個場景,假設在一個電商系統(tǒng)中,定時任務負責將庫存數據同步到第三方庫存管理系統(tǒng)。如果該任務在多個節(jié)點上同時執(zhí)行,可能會導致:
1、重復的庫存更新請求。
2、數據不同步或數據丟失。
3、系統(tǒng)負載過高,造成性能瓶頸。
使用 ShedLock 解決定時任務重復執(zhí)行問題
接下來,博主將通過 Spring Boot 和 ShedLock 來演示如何解決定時任務的重復執(zhí)行問題。希望能給大家一個參考!
? 添加依賴
首先確保我們的的 pom.xml 中包含必要的依賴。需要添加 Spring Boot Starter、Spring Scheduling 和 ShedLock 相關的依賴。
其中博主使用的是Mysql作為ShedLock初始化數據庫,引入shedlock-provider-jdbc-template
,大家可以根據自己的實際情況,根據作者的文檔選擇,如下圖:
<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>
對應版本兼容性依賴:
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 |
? 配置數據庫
ShedLock 需要在數據庫中存儲鎖的信息。這里博主使用 MySQL 來存儲鎖。首先,創(chuàng)建一個名為 shedlock 的表來存儲鎖數據。
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ū)分不同的任務。
- lock_until: 鎖的過期時間,任務完成后應釋放鎖。
- locked_at: 鎖的創(chuàng)建時間。
- locked_by: 鎖被哪個節(jié)點持有。
? 配置 ShedLock
接下來,我們在 Spring Boot
配置類中進行 ShedLock
的配置。以下是一個基本的配置類:
import net.javacrumbs.shedlock.spring.annotation.EnableSchedulerLock; import org.springframework.context.annotation.Configuration; import org.springframework.scheduling.annotation.EnableScheduling; @Configuration @EnableScheduling @EnableSchedulerLock(defaultLockAtMostFor = "30m") //默認鎖最大過期時間為 30 秒 public class ShedLockConfig { }
注解說明:
@EnableSchedulerLock: 啟用 ShedLock
功能,defaultLockAtMostFor
參數指定鎖的最大過期時間。如果任務執(zhí)行超過此時間,鎖將會被釋放。
@EnableScheduling: 啟用 Spring
的調度功能。
? 創(chuàng)建定時任務
接下來我們創(chuàng)建一個定時任務,并為其添加 ShedLock
鎖,確保在分布式環(huán)境中只有一個實例會執(zhí)行該任務。
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 注解來確保該任務只會在一個節(jié)點上執(zhí)行 */ @Scheduled(fixedRate = 5000) // 每 5 秒執(zhí)行一次 @SchedulerLock(name = "syncInventoryTask", lockAtMostFor = "5m", lockAtLeastFor = "5m") public void syncInventory() { System.out.println("同步庫存數據..."); // 模擬庫存同步操作 try { Thread.sleep(3000); // 模擬執(zhí)行耗時 3 秒 } catch (InterruptedException e) { Thread.currentThread().interrupt(); } System.out.println("庫存同步完成!"); } }
上述代碼中,我們在在 syncInventory 方法上,我們使用了 @SchedulerLock 注解,指定鎖的名稱 syncInventoryTask,并設置鎖的最大持續(xù)時間為 5 秒。
- name: 鎖的名稱,確保每個定時任務有唯一的名稱。
- lockAtMostFor: 鎖的最大過期時間,防止任務因為異常而無法釋放鎖。
- lockAtLeastFor: 鎖的最小持續(xù)時間,確保鎖至少保持一定時間。
? 配置數據庫連接
最后 application.properties 或 application.yml 配置我們的數據庫連接信息
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
? 啟動應用測試
最后我們啟動 Spring Boot
應用,查看控制臺輸出。你將會看到 syncInventory
任務每 5 秒鐘執(zhí)行一次,但只有一個節(jié)點能夠在任何時刻成功執(zhí)行該任務,避免了多節(jié)點并發(fā)執(zhí)行造成的問題。
總結
相信小伙伴們通過本文的示例,我們演示了如何使用 ShedLock
解決分布式系統(tǒng)中定時任務重復執(zhí)行的問題。ShedLock
提供了一種簡單有效的方式來確保在多實例部署的環(huán)境中,定時任務不會被重復執(zhí)行,避免了數據不一致等問題。
- 應用場景: 分布式定時任務、任務調度、數據同步等場景。
- 優(yōu)勢: 簡單易用、無需復雜配置、適配多種存儲方式(如 JDBC、MongoDB、Redis等)
到此這篇關于Spring Boot 整合 ShedLock 處理定時任務重復執(zhí)行的問題的文章就介紹到這了,更多相關Spring Boot ShedLock 定時任務重復執(zhí)行內容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關文章希望大家以后多多支持腳本之家!
相關文章
java實現(xiàn)用戶簽到BitMap功能實現(xiàn)demo
這篇文章主要為大家介紹了java實現(xiàn)用戶簽到BitMap功能實現(xiàn)demo,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進步,早日升職加薪2023-11-11