springBoot框架分佈式部署定時任務重複執行之解決方案

在集羣模式部署服務端時,會出現所有的定時任務在各自的節點處均會執行一遍,這顯然不符合實際的開發場景,針對這種問題,本文給出一種springboot集成shedlock的解決方案
第一步:引入相關包;

複製代碼

<!-- 負載均衡定時任務執行一次 -->
<dependency>
   <groupId>net.javacrumbs.shedlock</groupId>
   <artifactId>shedlock-spring</artifactId>
   <version>2.2.1</version>
</dependency>
<dependency>
   <groupId>net.javacrumbs.shedlock</groupId>
   <artifactId>shedlock-provider-jdbc-template</artifactId>
   <version>2.2.1</version>
</dependency>

複製代碼

注意:因爲本公司的項目持久化採用的mysql數據庫,所以引入的是JDBC數據庫進行協調

ShedLock還可以使用Mongo,Redis,Hazelcast,ZooKeeper等外部存儲進行協調,例如使用redis則引入下面的包(只嘗試過jdbc方式):

複製代碼

<dependency>
    <groupId>net.javacrumbs.shedlock</groupId>
    <artifactId>shedlock-provider-redis-spring</artifactId>
    <version>2.5.0</version>
</dependency>
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-data-redis</artifactId>
</dependency>

複製代碼

針對不同的協調方式,相關的配置信息可參考https://www.jianshu.com/p/9c6791b617a7

第二步:向數據庫中插入表shedlock;

複製代碼

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

)

複製代碼

第三步:在application.properties中添加數據庫配置信息

spring.datasource.url=jdbc:mysql://127.0.0.1:3306/test?useSSL=false&useUnicode=true&characterEncoding=utf-8&allowMultiQueries=true
spring.datasource.username=root1
spring.datasource.password=root1

第四步:添加配置類

複製代碼

import net.javacrumbs.shedlock.core.LockProvider;
import net.javacrumbs.shedlock.provider.jdbctemplate.JdbcTemplateLockProvider;
import net.javacrumbs.shedlock.spring.ScheduledLockConfiguration;
import net.javacrumbs.shedlock.spring.ScheduledLockConfigurationBuilder;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.scheduling.TaskScheduler;
import org.springframework.scheduling.annotation.EnableScheduling;

import javax.sql.DataSource;
import java.time.Duration;

@Configuration
@EnableScheduling
public class ShedlockConfig {

    @Bean
    public LockProvider lockProvider(DataSource dataSource) {
        return new JdbcTemplateLockProvider(dataSource);
    }

//    @Bean
//    public TaskScheduler taskScheduler(){
//        return new MySpecialTask​​Scheduler();
//    }

    @Bean
    public ScheduledLockConfiguration scheduledLockConfiguration(LockProvider lockProvider) {
        return ScheduledLockConfigurationBuilder
                .withLockProvider(lockProvider)
                .withPoolSize(10)
                .withDefaultLockAtMostFor(Duration.ofMinutes(10))
                .build();
    }
}

複製代碼

第五步:在啓動類上添加啓動註解,否則SchedulerLock不會生效

@EnableSchedulerLock(defaultLockAtMostFor = "PT50S")

第六步:添加@SchedulerLock到定時器業務方法入口

private static final int TWENTY_NINE_MIN = 29 * 60 * 1000;
@Scheduled(cron = "0 */30 * * * ?")
@SchedulerLock(name = "scheduledTask", lockAtMostFor = TWENTY_NINE_MIN, lockAtLeastFor = TWENTY_NINE_MIN)
public void scheduledTask() {
//    System.out.println(new Date() + "scheduledTask執行1次");
}

參數解釋:

name屬性:鎖名稱,必須指定,每次只能執行一個具有相同名字的任務,鎖名稱應該是全局唯一的;
lockAtMostFor屬性:設置鎖的最大持有時間,爲了解決如果持有鎖的節點掛了,無法釋放鎖,其他節點無法進行下一次任務;
lockAtMostForString屬性:成功執行任務的節點所能擁有的獨佔鎖的最長時間的字符串表達,例如“PT14M”表示爲14分鐘
lockAtLeastFor屬性:指定保留鎖的最短時間。主要目的是在任務非常短的且節點之間存在時鐘差異的情況下防止多個節點執行。這個屬性是鎖的持有時間。設置了多少就一定會持有多長時間,再此期間,下一次任務執行時,其他節點包括它本身是不會執行任務的
lockAtLeastForString屬性:成功執行任務的節點所能擁有的獨佔鎖的最短時間的字符串表達,例如“PT14M”表示爲14分鐘
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章