Springboot定時任務集成shedLock鎖

0、寫在前面

  最近在項目開發過程中,涉及到定時任務的編寫,定時任務大家都知道,在多服務器部署時,爲了防止同一時間同一任務多次執行的問題,通常需要使用分佈式定時任務進行處理,這部分對應的框架也很多,例如:xxl-job,power-job,elastic-job,但是由於考慮到當前所負責開發的項目體量,感覺使用上面所提到的分佈式定時任務框架太過於繁重,所以,這裏就使用了spring提供的scheduler定時任務註解方式開發,雖然此種方式開發簡便,但是同一時間同一任務多次執行的問題還是存在的,因此爲了解決此問題,這裏便引入了shedlock鎖組件。

  關於shedlock,這裏機翻了官網的一段說明,僅供簡單介紹,詳細可參看官網介紹(官網地址:https://github.com/lukas-krecan/ShedLock),官網說,shedlock只做一件事,就是確保計劃任務最多同時執行一次,如果正在一個節點上執行任務,它將獲取一個鎖,以防止從另一個節點(或線程)執行相同任務。請注意,過去一個任務已在一個節點上執行,則其他節點上的執行不會等待,只會跳過它;目前支持Mongo、JDBC數據庫、redis、hazelcast或zookeeper等存儲,shedlock不是分佈式調度框架,它只是一個鎖!!!

1、版本說明

Spring Cloud Alibaba版本:2022.0.0.0-RC1
Springboot版本:3.0.0
jdk版本:17.0.6
ShedLock版本:5.3.0
mysql版本:8.0.30

2、ShedLock使用

  由於項目使用的數據庫爲MySQL數據庫,所以這裏使用了shedlock-jdbcTemplate方式,關於其他存儲方式的使用可以參看官網(官網地址:https://github.com/lukas-krecan/ShedLock

2.1、創建數據庫

CREATE TABLE shedlock(name VARCHAR(64) NOT NULL COMMENT '鎖名稱', 
                      lock_until TIMESTAMP(3) NOT NULL COMMENT '釋放鎖時間',
                      locked_at TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP(3) COMMENT '獲取鎖時間',
                      locked_by VARCHAR(255) NOT NULL COMMENT '鎖提供者', 
                      PRIMARY KEY (name)
                     );

官網說明:

2.2、pom依賴

<properties>
    <shedlock.version>5.3.0</shedlock.version>
</properties>       

<dependency>
    <groupId>net.javacrumbs.shedlock</groupId>
    <artifactId>shedlock-core</artifactId>
    <version>${shedlock.version}</version>
</dependency>
<dependency>
    <groupId>net.javacrumbs.shedlock</groupId>
    <artifactId>shedlock-spring</artifactId>
    <version>${shedlock.version}</version>
</dependency>
<dependency>
    <groupId>net.javacrumbs.shedlock</groupId>
    <artifactId>shedlock-provider-jdbc-template</artifactId>
    <version>${shedlock.version}</version>
    <exclusions>
        <exclusion>
            <artifactId>spring-jdbc</artifactId>
            <groupId>org.springframework</groupId>
        </exclusion>
    </exclusions>
</dependency>
<dependency>
    <groupId>org.springframework</groupId>
    <artifactId>spring-jdbc</artifactId>
    <version>6.0.6</version>
</dependency>

說明:

這裏單獨引入spring-jdbc依賴的原因如下:

  由於Springboot 3.0.0版本使用的Spring 6.0.2版本在針對數據表中存在數據插入時,會報Duplicate key violation gets translated to DataIntegrityViolationException instead of DuplicateKeyException異常(issue地址:https://github.com/spring-projects/spring-framework/issues/29511),導致shedlock執行時,報類似異常,針對此問題,spring官方也進行了修復(https://github.com/spring-projects/spring-framework/releases/tag/v6.0.3),但是由於項目依賴管理的父pom定義了spring的版本,這裏不方便修改父pom的依賴信息,所以就在項目中單獨添加了spring-jdbc的依賴,已解決此異常。異常信息如下:

 Spring官方修復信息:

2.3、添加配置類

@Configuration
public class SchedulerLockConfig {
    @Bean
    public LockProvider lockProvider(DataSource dataSource) {
        return new JdbcTemplateLockProvider(
                JdbcTemplateLockProvider.Configuration.builder()
                        .withJdbcTemplate(new JdbcTemplate(dataSource))
                        .build()
        );
    }
}

2.4、啓動類配置

在Springboot啓動類上添加如下註解:

@EnableScheduling
@EnableSchedulerLock(defaultLockAtMostFor = "PT30S")

說明:

  defaultLockAtMostFor爲鎖默認的最大鎖定時間,可以根據需要賦值,其中S爲秒,M爲分鐘,H爲小時,D爲天,PT爲ISO-8601期限格式,這部分可以參看Duration.parse()文檔

2.5、定時任務方法配置

import net.javacrumbs.shedlock.spring.annotation.SchedulerLock;

...

@Scheduled(cron = "0 */2 * * * ?")
@SchedulerLock(name = "scheduledTaskName", lockAtLeastFor = "PT60S", lockAtMostFor = "PT60S")
@Transactional(rollbackFor = Exception.class)
public void scheduledTask() {
    // 代碼省略......
}

shedlock數據表數據:

2.6、註解說明

@SchedulerLock:

  使用此註解的方法被鎖定,通過指定鎖的名稱name屬性(鎖名稱唯一),保證同一時間只能執行一個任務

關於lockAtLeastFor、lockAtMostFor屬性說明:

  當第一個微服務執行定時任務的時候,會將此定時任務進行鎖操作,然後其他的定時任務就不會再執行,鎖操作有一定的時長,超過這個時長以後,鎖釋放,然後所有的定時任務進行爭搶下一個定時任務的執行權利,如此循環。其中兩個配置lockAtMostFor和lockAtLeastFor,保證了在一個定時任務的區間內只有一個定時任務在執行,同時也保證了即便是其中的一個定時任務掛掉了,到一定的時間以後,鎖也會釋放,其他的定時任務依舊會進行執行權的爭奪,執行定時任務。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章