1. 程式人生 > >Springboot寫定時任務《Springboot學習八》

Springboot寫定時任務《Springboot學習八》

spring boot整合定時任務

//看之前本部落格是有專案地址的:
https://github.com/HouChenggong/springboot_schedule

專案地址: 傳送門

定時任務或者說定時排程,是系統中比較普遍的一個功能,例如資料歸檔、清理,資料定時同步(非實時),定時收發,流量控制等等都需要用到定時任務,常見的定時排程框架有Quartz、TBSchedule等。

同樣,Spring自3.0版本起也增加了任務排程功能Schedule,它好比是一個輕量級的Quartz,使用起來方便、簡潔,且不需要依賴其他的JAR包。之所以說它是輕量級Quartz,是因為在現如今遍地分散式的大環境下,Spring自帶的Schedule不支援分散式部署,所以若是分散式環境開發請忽略此文章,可以選用Quartz、TBSchedule等,且一般稍大點的公司都有獨立的統一排程中心。

開始配置

1. 啟動類新增定時任務註解
@EnableScheduling
@SpringBootApplication
//@MapperScan("com.pf.org.cms.mapper")
public class CmsApplication {

    public static void main(String[] args) {
        SpringApplication.run(CmsApplication.class, args);
    }
}
2. 編寫配置類

編寫具體的定時任務元件(@Component註解),並且在需要定時排程的方法上新增@Scheduled觸發器。Spring實現了Quartz簡單的和cron表示式兩種觸發方式,這裡我們用cron表示式,”0/20 * * * * ?”表示每20秒觸發一次,具體cron表示式邏輯可以自行百度。

@Component
public class MyStaticTask {

    /**
     * 固定cron配置定時任務
     */
    @Scheduled(cron = "0/20 * * * * ?")
    public void doTask(){
        System.out.println("執行了MyStaticTask,時間為:"+new Date(System.currentTimeMillis()));
    }
}
 

以上,一個簡單的定時任務便已完成,重新啟動專案,你就會發現定時器了。

但是觸發器是硬編碼,相信肯定滿足不了絕大部分業務場景,同樣Spring提供了SchedulingConfigurer介面,下面介紹可配置、動態修改觸發器的定時任務。


多執行緒定時任務

@Component
public class MyDynamicTask implements SchedulingConfigurer {
    private static Logger log = LoggerFactory.getLogger(MyDynamicTask.class);

    @Override
    public void configureTasks(ScheduledTaskRegistrar scheduledTaskRegistrar) {
        //設定一個長度10的定時任務執行緒池
        //所有的定時任務都放在一個執行緒池中,定時任務啟動時使用不同都執行緒。
        scheduledTaskRegistrar.setScheduler(Executors.newScheduledThreadPool(10));
    }

多執行緒定時任務結果日誌

可以發現下面的定時任務都在任務池中,pool-1-thread-4、pool-1-thread-3、pool-1-thread-2

2018-11-05 11:55:00 284841 [pool-1-thread-4] INFO  c.p.o.c.configuration.MyStaticTask - 定時任務1 開始執行... 
2018-11-05 11:55:00 284841 [pool-1-thread-3] INFO  c.p.o.c.configuration.MyStaticTask - 定時任務2 開始執行... 
2018-11-05 11:55:00 284841 [pool-1-thread-1] INFO  c.p.o.c.configuration.MyStaticTask - 定時任務0:執行了MyStaticTask,時間為:Mon Nov 05 11:55:00 CST 2018 
2018-11-05 11:55:00 284841 [pool-1-thread-2] INFO  c.p.o.c.configuration.MyDynamicTask - 執行了MyDynamicTask,時間為:Mon Nov 05 11:55:00 CST 2018 

可配置的定時任務元件需要實現SchedulingConfigurer介面中的configureTasks方法,該方法有兩個入參(Runnable task, Trigger trigger),第一個為我們任務的具體邏輯實現,第二個為觸發器,動態的定時任務則意味著Trigger需要動態獲取,由於之前我們已經整合redis,因此這裡我們從redis獲取相關配置。

//是從redis裡面取資料,沒有安裝redis的可以參考這篇文章
。。。。

String newCron = redisManager.getStr("cms:MyDynamicTask");
package com.pf.org.cms.configuration;

import com.alibaba.druid.util.StringUtils;
import com.pf.org.cms.exception.CmsException;
import com.pf.org.cms.manage.RedisManager;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.scheduling.Trigger;
import org.springframework.scheduling.TriggerContext;
import org.springframework.scheduling.annotation.SchedulingConfigurer;
import org.springframework.scheduling.config.ScheduledTaskRegistrar;
import org.springframework.scheduling.support.CronTrigger;
import org.springframework.stereotype.Component;

import java.util.Date;

@Component
public class MyDynamicTask implements SchedulingConfigurer {
    private static Logger log = LoggerFactory.getLogger(MyDynamicTask.class);

    @Autowired
    RedisManager redisManager;

    private static String cron;

    @Override
    public void configureTasks(ScheduledTaskRegistrar scheduledTaskRegistrar) {
        scheduledTaskRegistrar.addTriggerTask(doTask(), getTrigger());
    }

    private Runnable doTask() {
        return new Runnable() {
            @Override
            public void run() {
                // 業務邏輯
                log.info("執行了MyDynamicTask,時間為:" + new Date(System.currentTimeMillis()));
            }
        };
    }

    private Trigger getTrigger() {
        return new Trigger() {
            @Override
            public Date nextExecutionTime(TriggerContext triggerContext) {
                // 觸發器
                CronTrigger trigger = new CronTrigger(getCron());
                return trigger.nextExecutionTime(triggerContext);
            }
        };
    }

    public String getCron() {
        String newCron = redisManager.getStr("cms:MyDynamicTask");
        if (StringUtils.isEmpty(newCron)) {
            throw new CmsException("The config cron expression is empty");
        }
        if (!newCron.equals(cron)) {
            log.info(new StringBuffer("Cron has been changed to:'").append(newCron).append("'. Old cron was:'").append(cron).append("'").toString());
            cron = newCron;
        }
        return cron;
    }
}


安裝redis並在redis的db2資料庫中添加了

key:cms:MyDynamicTask
value:abcDEF

之後發生瞭如下的錯誤

Cron expression must consist of 6 fields

原因呢:其實就是你的value裡面要是定時器的語法,我們的自定義語句中其實就是去redis裡面去字串,這個字串是定時器指令碼,而不是任意的這裡貼出我的指令碼,其實和最初的指令碼一樣

key:cms:MyDynamicTask
value:0/20 * * * * ?
專案地址:

https://github.com/HouChenggong/springboot_schedule