最新国产好看的视频,伊人天堂AV在线,国产Aaaaaa视频,蜜臀视频在线观看一区,人妻av色图,密臀久久久精品影片,青青视频免费观看毛片,久草在线观看视,国产三级精品色情在线

SpringBoot定時(shí)任務(wù)多實(shí)例互斥執(zhí)行的完整指南

 更新時(shí)間:2026年01月26日 08:33:31   作者:風(fēng)象南  
Spring Boot 的 @Scheduled 寫(xiě)定時(shí)任務(wù)很方便,但多實(shí)例部署時(shí)有個(gè)問(wèn)題,同一個(gè)定時(shí)任務(wù)會(huì)在每臺(tái)機(jī)器上都觸發(fā)執(zhí)行,下面我們就來(lái)看看如何解決吧

Spring Boot 的 @Scheduled 寫(xiě)定時(shí)任務(wù)很方便,但多實(shí)例部署時(shí)有個(gè)問(wèn)題:同一個(gè)定時(shí)任務(wù)會(huì)在每臺(tái)機(jī)器上都觸發(fā)執(zhí)行。

比如部署了兩臺(tái)應(yīng)用服務(wù)器,凌晨 2 點(diǎn)的數(shù)據(jù)統(tǒng)計(jì)任務(wù)會(huì)同時(shí)跑兩遍,數(shù)據(jù)重復(fù)、文件重復(fù)生成。

解決這個(gè)問(wèn)題通常有幾種思路。

常見(jiàn)方案

方案一:?jiǎn)螜C(jī)執(zhí)行

只在一臺(tái)指定的機(jī)器上跑任務(wù):

@Scheduled(cron = "0 0 2 * * ?")
public void scheduledTask() {
    String hostname = InetAddress.getLocalHost().getHostName();
    if (!"app-server-01".equals(hostname)) {
        return;
    }
    // do something
}

問(wèn)題很明顯:那臺(tái)機(jī)器掛了,任務(wù)就不執(zhí)行了。

方案二:Redis 分布式鎖

用 Redis 的 SETNX 實(shí)現(xiàn)互斥(或者Redission):

@Scheduled(cron = "0 0 2 * * ?")
public void scheduledTask() {
    Boolean locked = stringRedisTemplate.opsForValue()
        .setIfAbsent("task:data-sync", "1", 10, TimeUnit.MINUTES);
    if (Boolean.FALSE.equals(locked)) {
        return;
    }
    try {
        // do something
    } finally {
        stringRedisTemplate.delete("task:data-sync");
    }
}

能用,但每個(gè)任務(wù)都要寫(xiě)一遍加鎖釋放邏輯,而且需要項(xiàng)目里有 Redis。

ShedLock 方案

ShedLock 是一個(gè)專門(mén)解決定時(shí)任務(wù)重復(fù)執(zhí)行問(wèn)題的框架,支持多種存儲(chǔ)后端(數(shù)據(jù)庫(kù)、Redis、MongoDB 等)。

核心思路:在存儲(chǔ)層記錄每個(gè)任務(wù)的鎖狀態(tài),任務(wù)執(zhí)行前先搶鎖,搶到了才執(zhí)行。

集成步驟

1. 添加依賴

<dependency>
    <groupId>net.javacrumbs.shedlock</groupId>
    <artifactId>shedlock-spring</artifactId>
    <version>4.42.0</version>
</dependency>
<dependency>
    <groupId>net.javacrumbs.shedlock</groupId>
    <artifactId>shedlock-provider-jdbc-template</artifactId>
    <version>4.42.0</version>
</dependency>

2. 創(chuàng)建鎖表

CREATE TABLE shedlock (
    name VARCHAR(64) NOT NULL,
    lock_until TIMESTAMP(3) NOT NULL,
    locked_at TIMESTAMP(3) NOT NULL,
    locked_by VARCHAR(255) NOT NULL,
    PRIMARY KEY (name)
);

3. 配置 LockProvider

@Configuration
@EnableSchedulerLock(defaultLockAtMostFor = "10m")
public class ShedLockConfig {

    @Bean
    public LockProvider lockProvider(DataSource dataSource) {
        return new JdbcTemplateLockProvider(
            JdbcTemplateLockProvider.Configuration.builder()
                .withDataSource(dataSource)
                .withTableName("shedlock")
                .build()
        );
    }
}

4. 給定時(shí)任務(wù)加注解

@Scheduled(cron = "0 0 2 * * ?")
@SchedulerLock(name = "dataSyncTask", lockAtMostFor = "5m")
public void syncData() {
    // do something
}

完成。多臺(tái)服務(wù)器部署時(shí),只有搶到鎖的那臺(tái)會(huì)執(zhí)行任務(wù)。

注解參數(shù)說(shuō)明

@SchedulerLock 有幾個(gè)關(guān)鍵參數(shù):

name:鎖名稱,相同 name 的任務(wù)會(huì)互斥執(zhí)行。建議用任務(wù)名來(lái)命名,保持唯一。

lockAtMostFor:鎖的最大持有時(shí)間。

這是為了防止任務(wù)執(zhí)行過(guò)程中機(jī)器宕機(jī),導(dǎo)致鎖永遠(yuǎn)不釋放。比如設(shè)置 5m,即使任務(wù)執(zhí)行超時(shí),鎖也會(huì)在 5 分鐘后自動(dòng)過(guò)期。

一般按任務(wù)預(yù)期執(zhí)行時(shí)間的 2 倍左右設(shè)置,留些余量。

lockAtLeastFor:鎖的最小持有時(shí)間。

這是為了防止任務(wù)執(zhí)行太快,鎖立即釋放被其他機(jī)器搶到。

比如定時(shí)任務(wù)每分鐘執(zhí)行一次,任務(wù) 5 秒就跑完了。如果沒(méi)有這個(gè)參數(shù),其他機(jī)器可能會(huì)在同一分鐘內(nèi)再次搶到鎖執(zhí)行。

這種情況下可以設(shè)置 lockAtLeastFor = "1m",確保鎖保持到下一分鐘。

實(shí)現(xiàn)原理

ShedLock 的實(shí)現(xiàn)邏輯不復(fù)雜。

任務(wù)執(zhí)行前,會(huì)向數(shù)據(jù)庫(kù)插入或更新鎖記錄:

INSERT INTO shedlock (name, lock_until, locked_at, locked_by)
VALUES ('dataSyncTask', '2025-01-25 02:05:00', NOW(), '192.168.1.10')
ON DUPLICATE KEY UPDATE
  lock_until = '2025-01-25 02:05:00',
  locked_at = NOW(),
  locked_by = '192.168.1.10'
WHERE lock_until < NOW();

關(guān)鍵是最后的 WHERE lock_until < NOW() 條件:

  • 如果當(dāng)前鎖已過(guò)期,UPDATE 成功,搶到鎖
  • 如果當(dāng)前鎖未過(guò)期,UPDATE 影響行數(shù)為 0,搶鎖失敗,任務(wù)不執(zhí)行

任務(wù)執(zhí)行完成后不需要主動(dòng)釋放鎖,等待 lock_until 時(shí)間到期即可。

適用場(chǎng)景

ShedLock 的定位很明確:專門(mén)為定時(shí)任務(wù)設(shè)計(jì)的分布式鎖框架。

適合

  • 定時(shí)任務(wù)需要互斥執(zhí)行,避免重復(fù)
  • 希望用注解方式簡(jiǎn)化鎖的代碼邏輯
  • 需要自動(dòng)鎖過(guò)期機(jī)制,防止死鎖

不適合

  • 高并發(fā)搶鎖的業(yè)務(wù)場(chǎng)景(比如秒殺、庫(kù)存扣減),ShedLock 不是為此設(shè)計(jì)
  • 需要可重入鎖、讀寫(xiě)鎖等復(fù)雜特性
  • 需要精確控制鎖獲取和釋放時(shí)機(jī)的業(yè)務(wù)邏輯

ShedLock 和通用分布式鎖是互補(bǔ)關(guān)系,不是替代關(guān)系。如果你的業(yè)務(wù)代碼里需要手動(dòng)加鎖解鎖,用 Redisson 或手動(dòng)實(shí)現(xiàn) Redis SETNX 更合適。但如果只是為了解決定時(shí)任務(wù)重復(fù)執(zhí)行的問(wèn)題,ShedLock 是更簡(jiǎn)潔的方案。

幾個(gè)注意事項(xiàng)

1. 存儲(chǔ)后端選擇

本文演示用的是 JDBC 方式(基于數(shù)據(jù)庫(kù)表)。ShedLock 還支持 Redis、MongoDB、ZooKeeper、Hazelcast 等多種存儲(chǔ)后端,根據(jù)項(xiàng)目現(xiàn)有技術(shù)棧選擇即可。

2. 表名自定義(數(shù)據(jù)庫(kù)存儲(chǔ)時(shí))

如果用 JDBC 作為存儲(chǔ)后端,默認(rèn)表名是 shedlock,可以按需修改: 3. 機(jī)器標(biāo)識(shí)

locked_by 字段記錄是哪臺(tái)機(jī)器拿到的鎖,默認(rèn)是主機(jī)名??梢宰远x成更有意義的標(biāo)識(shí):

.withLockedByValue("app-" + getServerIp())

4. 主從/多數(shù)據(jù)源場(chǎng)景(數(shù)據(jù)庫(kù)存儲(chǔ)時(shí))

如果項(xiàng)目有多個(gè)數(shù)據(jù)源,確保 ShedLock 用的是主庫(kù),避免主從延遲導(dǎo)致的鎖問(wèn)題。

總結(jié)

ShedLock 是一個(gè)專門(mén)為定時(shí)任務(wù)設(shè)計(jì)的分布式鎖框架:

  • 優(yōu)點(diǎn):注解式使用、集成簡(jiǎn)單、自動(dòng)鎖過(guò)期、支持多種存儲(chǔ)后端
  • 局限性:只適用于定時(shí)任務(wù)場(chǎng)景,不適用于通用業(yè)務(wù)加鎖

和手動(dòng)寫(xiě) Redis 分布式鎖相比,ShedLock 把定時(shí)任務(wù)鎖的邏輯抽象出來(lái)了,代碼更簡(jiǎn)潔。但如果你需要的是通用業(yè)務(wù)鎖,還是用 Redisson 或手寫(xiě) SETNX 更合適。

以上就是SpringBoot定時(shí)任務(wù)多實(shí)例互斥執(zhí)行的完整指南的詳細(xì)內(nèi)容,更多關(guān)于SpringBoot定時(shí)任務(wù)的資料請(qǐng)關(guān)注腳本之家其它相關(guān)文章!

相關(guān)文章

  • Java中Thread類基本用法詳解

    Java中Thread類基本用法詳解

    Java中的Thread類是用于創(chuàng)建和管理線程的類,Thread類提供了許多方法來(lái)管理線程,包括啟動(dòng)線程、中斷線程、暫停線程等,下面這篇文章主要給大家介紹了關(guān)于Java中Thread類基本用法的相關(guān)資料,需要的朋友可以參考下
    2023-06-06
  • Jedis出現(xiàn)connection timeout問(wèn)題解決方法(JedisPool連接池使用實(shí)例)

    Jedis出現(xiàn)connection timeout問(wèn)題解決方法(JedisPool連接池使用實(shí)例)

    這篇文章主要介紹了Jedis出現(xiàn)connection timeout問(wèn)題解決方法,使用Jedis的JedisPool連接池解決了這個(gè)問(wèn)題,需要的朋友可以參考下
    2014-05-05
  • springboot集成mybatisPlus+多數(shù)據(jù)源的實(shí)現(xiàn)示例

    springboot集成mybatisPlus+多數(shù)據(jù)源的實(shí)現(xiàn)示例

    這篇文章主要介紹了springboot集成mybatisPlus+多數(shù)據(jù)源的實(shí)現(xiàn)示例,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧
    2020-12-12
  • Mybatis如何實(shí)現(xiàn)延遲加載及緩存

    Mybatis如何實(shí)現(xiàn)延遲加載及緩存

    這篇文章主要介紹了Mybatis如何實(shí)現(xiàn)延遲加載及緩存,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友可以參考下
    2020-10-10
  • 通過(guò)Java實(shí)現(xiàn)反向代理集群服務(wù)的平滑分配

    通過(guò)Java實(shí)現(xiàn)反向代理集群服務(wù)的平滑分配

    這篇文章主要介紹了如何通過(guò)Java語(yǔ)言,自己編寫(xiě)的平滑加權(quán)輪詢算法,結(jié)合線程池和Socket?網(wǎng)絡(luò)編程等,并實(shí)現(xiàn)反向代理集群服務(wù)的平滑分配,需要的可以參考一下
    2022-04-04
  • Springboot中如何通過(guò)yml為實(shí)體類注入屬性

    Springboot中如何通過(guò)yml為實(shí)體類注入屬性

    這篇文章主要介紹了Springboot中如何通過(guò)yml為實(shí)體類注入屬性,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教
    2022-05-05
  • idea?maven依賴引入失效無(wú)法正常導(dǎo)入依賴問(wèn)題的解決方法

    idea?maven依賴引入失效無(wú)法正常導(dǎo)入依賴問(wèn)題的解決方法

    有時(shí)候idea導(dǎo)入一個(gè)新項(xiàng)目,或者pom文件修改(新增)了依賴,pom文件和代碼會(huì)報(bào)紅,提示依賴包不存在,下面這篇文章主要給大家介紹了關(guān)于idea?maven依賴引入失效無(wú)法正常導(dǎo)入依賴問(wèn)題的解決方法,需要的朋友可以參考下
    2023-04-04
  • Java Swing組件BoxLayout布局用法示例

    Java Swing組件BoxLayout布局用法示例

    這篇文章主要介紹了Java Swing組件BoxLayout布局用法,結(jié)合實(shí)例形式分析了Swing使用BoxLayout容器進(jìn)行布局的相關(guān)方法與操作技巧,需要的朋友可以參考下
    2017-11-11
  • 利用Spring框架為自己的校園卡充值(推薦)

    利用Spring框架為自己的校園卡充值(推薦)

    這篇文章主要介紹了利用Spring框架為自己的校園卡充值,本次實(shí)驗(yàn)主要運(yùn)用了Spring的控制反轉(zhuǎn)(IOC)和依賴注入(DI)等知識(shí),通過(guò)利用Spring框架編寫(xiě)Java程序,實(shí)現(xiàn)學(xué)生卡的單次充值,如需對(duì)學(xué)生卡進(jìn)行多次充值,可將部分代碼修改即可實(shí)現(xiàn),需要的朋友可以參考下
    2022-10-10
  • 關(guān)于IDEA MybatisX插件的使用小技巧

    關(guān)于IDEA MybatisX插件的使用小技巧

    這篇文章主要介紹了關(guān)于MybatisX插件的使用小技巧,MybatisX是IDEA的一款第三方插件,可以極大地提升我們的開(kāi)發(fā)效率,一起來(lái)看看吧
    2023-03-03

最新評(píng)論

新乐市| 孟津县| 吉木乃县| 外汇| 霍城县| 镇巴县| 林州市| 炉霍县| 灵丘县| 西林县| 长顺县| 合作市| 古交市| 二手房| 涿鹿县| 静乐县| 陈巴尔虎旗| 和顺县| 德清县| 抚松县| 邢台县| 霍林郭勒市| 体育| 隆林| 中阳县| 宣化县| 龙门县| 婺源县| 龙江县| 甘肃省| 常德市| 承德市| 德庆县| 和政县| 闵行区| 临潭县| 咸丰县| 阳西县| 富裕县| 海丰县| 桑日县|