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