Java中實現分布式定時任務


定時器Scheduler在平時使用比較頻繁,在springboot中,配置好@Scheduled和@EnableScheduling之后,定時器就能正常執行,實現定時任務的功能。

 

但是在這樣的情況下:如果開發的服務需要水平部署實現負載均衡,那么定時任務就會同時在多個服務實例上運行,那么一方面,可能由於定時任務的邏輯處理需要訪問公共資源從而造成並發問題;另一方面,就算沒有並發問題,那么一個同樣的任務多個服務實例同時執行,也會造成資源的浪費。因此需要一種機制來保證多個服務實例之間的定時任務正常、合理地執行。

本文以shedlock為例,來實現分布式定時任務的控制。
 

ShedLock可以保證多個同樣的定時任務在多個服務實例之間最多只執行一次,是一個在分布式環境中保證定時任務合理執行的框架,我們可以叫它分布式定時任務鎖。

ShedLock的實現原理是采用公共存儲實現的鎖機制,使得同一時間點只有第一個執行定時任務的服務實例能執行成功,並在公共存儲中存儲"我正在執行任務,從什么時候(預計)執行到什么時候",其他服務實例執行時如果發現任務正在執行,則直接跳過本次執行,從而保證同一時間一個任務只被執行一次。

ShedLock的公共存儲目前支持的有:
Monogo
DynamoDB
JdbcTemplate
ZooKeeper (using Curator)
Redis (using Spring RedisConnectionFactory)
Redis (using Jedis)
Hazelcast
第一步引入依賴
<!-- shedlock start -->
        <dependency>
            <groupId>net.javacrumbs.shedlock</groupId>
            <artifactId>shedlock-spring</artifactId>
            <version>4.11.1</version>
        </dependency>

        <dependency>
            <groupId>net.javacrumbs.shedlock</groupId>
            <artifactId>shedlock-provider-jdbc-template</artifactId>
            <version>4.11.1</version>
        </dependency>
<!-- shedlock end -->

 

第二步添加配置類
import net.javacrumbs.shedlock.core.LockProvider;
import net.javacrumbs.shedlock.provider.jdbctemplate.JdbcTemplateLockProvider;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.jdbc.core.JdbcTemplate;
import javax.annotation.Resource;
import javax.sql.DataSource;
import java.util.TimeZone;

/**
 * @descrition shedlock配置類
 * @since 2021-01-10 22:39
 */
@Configuration
public class ShedLockConfig {
    @Resource
    private DataSource dataSource;

    /**
     * @description
     * @date 2021/1/10 22:39
     */
    @Bean
    public LockProvider lockProvider() {
        return new JdbcTemplateLockProvider(
                JdbcTemplateLockProvider.Configuration.builder()
                        .withJdbcTemplate(new JdbcTemplate(dataSource))
                        .withTimeZone(TimeZone.getTimeZone("GMT+8"))
                        .build()
        );
    }
}

 

第三步,添加公共存儲,前面我們說過shedlock支持多種公共存儲作為鎖,本文我們以mysql為例
 
CREATE TABLE shedlock (
    NAME VARCHAR ( 64 ) NOT NULL,
    lock_until TIMESTAMP ( 3 ) NOT NULL,
    locked_at TIMESTAMP ( 3 ) NOT NULL DEFAULT CURRENT_TIMESTAMP ( 3 ),
    locked_by VARCHAR ( 255 ) NOT NULL,
    PRIMARY KEY ( NAME ) 
);

 

第四步,添加具體任務類
import lombok.extern.slf4j.Slf4j;
import net.javacrumbs.shedlock.spring.annotation.SchedulerLock;
import org.springframework.scheduling.annotation.Scheduled;
import org.springframework.stereotype.Component;
import java.text.SimpleDateFormat;
import java.util.Date;


/**
 * @author shane
 * @date 2021/1/10 23:39
 */
@Slf4j
@Component
public class TestJob {
    /**
     * @description 每隔1min打印一次
     * @date 2021/1/10 23:39
     */
    @Scheduled(cron = "0 0/1 * * * ?")
    // lockAtMostFor為鎖默認持有時間,會覆蓋啟動類中的默認持有時間
    @SchedulerLock(name = "demo", lockAtMostFor = "70m")
    public void print() throws InterruptedException {
        SimpleDateFormat df = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");//設置日期格式
        log.warn("當前時間:"+df.format(new Date()));
    }
}

 

接着,我們復制一份代碼,分別啟動兩個實例來看結果
 
 
 
 
 
 
 
數據庫記錄
 
 
 
@SchedulerLock注解參數說明
name:定時任務的名字,就是數據庫中的內個主鍵
lockAtMostFor:鎖的最大時間單位為毫秒
lockAtLeastFor:鎖的最小時間單位為毫秒

對了,還有啟動類的配置
@SpringBootApplication
@MapperScan("com.example.test.mapper")
@EnableScheduling
@EnableSchedulerLock(defaultLockAtMostFor = "10m")  // 默認的鎖的時間
public class TestApplication {

    public static void main(String[] args) {
        SpringApplication.run(TestApplication.class, args);
    }

}

 

 
參考出處: https://www.jianshu.com/p/941416645606
shedlock的github地址:https://github.com/lukas-krecan/ShedLock
 
 
 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM