真香!SpringBoot官方支援任務排程框架了!
推薦閱讀:
摘要
之前我們講過一個分散式任務排程框架PowerJob,可以通過視覺化的方式來進行任務排程。但是有時候我們只是需要一個輕量級的任務排程功能,而PowerJob需要搭建排程中心未免有些重,這時候SpringBoot官方支援的任務排程框架Quartz就派上用場了!本文主要介紹Quartz在SpringBoot中的使用,讓你在實現任務排程上有更多的選擇!
Quartz簡介
Quartz是一款功能強大的開源任務排程框架,幾乎可以整合到任何Java應用程式中(小到單機應用,大到分散式應用)。Quartz可用於建立簡單或複雜的任務排程,用以執行數以萬計的任務。任務被定義為標準化的Java元件,Java編寫的任務都可以被執行。
核心概念
Quartz中有一些比較核心的概念,理解它們對使用Quartz很有幫助!
- Scheduler(排程器):Quartz中的任務排程器,通過Trigger和JobDetail可以用來排程、暫停和刪除任務。
- Trigger(觸發器):Quartz中的觸發器,可以通過CRON表示式來指定任務執行的時間,時間到了會自動觸發任務執行。
- JobDetail(任務詳情):Quartz中需要執行的任務詳情,包括了任務的唯一標識和具體要執行的任務,可以通過JobDataMap往任務中傳遞資料。
- Job(任務):Quartz中具體的任務,包含了執行任務的具體方法。
CRON表示式
Cron表示式是一個字串,包括6~7個時間元素,在Quartz中可以用於指定任務的執行時間。
CRON的語法格式
Seconds Minutes Hours DayofMonth Month DayofWeek
複製程式碼
CRON格式中每個時間元素的說明
時間元素 | 可出現的字元 | 有效數值範圍 |
---|---|---|
Seconds | , - * / | 0-59 |
Minutes | , - * / | 0-59 |
Hours | , - * / | 0-23 |
DayofMonth | , - * / ? L W | 0-31 |
Month | , - * / | 1-12 |
DayofWeek | , - * / ? L # | 1-7或SUN-SAT |
CRON格式中特殊字元說明
字元 | 作用 | 舉例 |
---|---|---|
, | 列出列舉值 | 在Minutes域使用5,10,表示在5分和10分各觸發一次 |
- | 表示觸發範圍 | 在Minutes域使用5-10,表示從5分到10分鐘每分鐘觸發一次 |
* | 匹配任意值 | 在Minutes域使用*, 表示每分鐘都會觸發一次 |
/ | 起始時間開始觸發,每隔固定時間觸發一次 | 在Minutes域使用5/10,表示5分時觸發一次,每10分鐘再觸發一次 |
? | 在DayofMonth和DayofWeek中,用於匹配任意值 | 在DayofMonth域使用?,表示每天都觸發一次 |
# | 在DayofMonth中,確定第幾個星期幾 | 1#3表示第三個星期日 |
L | 表示最後 | 在DayofWeek中使用5L,表示在最後一個星期四觸發 |
W | 表示有效工作日(週一到週五) | 在DayofMonth使用5W,如果5日是星期六,則將在最近的工作日4日觸發一次 |
線上CRON表示式生成器
其實CRON表示式無需多記,需要使用的時候直接使用線上生成器就可以了,地址:cron.qqe2.com/
整合SpringBoot使用
接下來我們講下如何在SpringBoot中使用Quartz來實現任務排程,在電商系統中往往會有需要定時傳送郵件或者站內信的需求,我們以此為場景來實現下!
- Quartz儲存任務資訊有兩種方式,使用記憶體或者使用資料庫來儲存,這裡我們採用資料庫儲存的方式,首先需要新建Quartz的相關表,建表指令碼在專案的
resources
目錄下,名稱為tables_mysql.sql
,建立成功後資料庫中多出11張表;
- 接下來在
pom.xml
中新增Quartz的相關依賴即可,SpringBoot官方已經給我們提供好了相關Starter;
<!--SpringBoot整合QuartZ-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-quartz</artifactId>
</dependency>
- 在
application.yml
中新增Quartz相關配置,配置說明直接看註釋就好了,主要是對scheduler
、jobStore
和threadPool
進行配置;
spring:
quartz:
job-store-type: jdbc # quartz任務儲存型別:jdbc或memory
wait-for-jobs-to-complete-on-shutdown: true # 關閉時等待任務完成
overwrite-existing-jobs: true # 可以覆蓋已有的任務
properties: # quartz原生配置
org:
quartz:
scheduler:
instanceName: scheduler # 排程器例項名稱
instanceId: AUTO # 排程器例項ID自動生成
jobStore:
class: org.quartz.impl.jdbcjobstore.JobStoreTX # 排程資訊儲存處理類
driverDelegateClass: org.quartz.impl.jdbcjobstore.StdJDBCDelegate # 使用完全相容JDBC的驅動
tablePrefix: QRTZ_ # quartz相關表字首
useProperties: false # 是否將JobDataMap中的屬性轉為字串儲存
threadPool:
class: org.quartz.simpl.SimpleThreadPool # 指定執行緒池實現類,對排程器提供固定大小的執行緒池
threadCount: 10 # 設定併發執行緒數量
threadPriority: 5 # 指定執行緒優先順序
- 建立任務排程業務介面,定義好三個方法,分別為通過CRON表示式排程任務、排程指定時間的任務和取消定時任務;
/**
* Quartz定時任務操作類
* Created by macro on 2020/9/27.
*/
public interface ScheduleService {
/**
* 通過CRON表示式排程任務
*/
String scheduleJob(Class<? extends Job> jobBeanClass, String cron, String data);
/**
* 排程指定時間的任務
*/
String scheduleFixTimeJob(Class<? extends Job> jobBeanClass, Date startTime, String data);
/**
* 取消定時任務
*/
Boolean cancelScheduleJob(String jobName);
}
- 建立任務排程業務實現類,通過
Scheduler
、CronTrigger
、JobDetail
的API實現相關方法;
/**
* Quartz定時任務操作實現類
* Created by macro on 2020/9/27.
*/
@Slf4j
@Service
public class ScheduleServiceImpl implements ScheduleService {
@Autowired
private Scheduler scheduler;
private String defaultGroup = "default_group";
@Override
public String scheduleJob(Class<? extends Job> jobBeanClass, String cron, String data) {
// 建立需要執行的任務
String jobName = UUID.fastUUID().toString();
JobDetail jobDetail = JobBuilder.newJob(jobBeanClass)
.withIdentity(jobName, defaultGroup)
.usingJobData("data", data)
.build();
//建立觸發器,指定任務執行時間
CronTrigger cronTrigger = TriggerBuilder.newTrigger()
.withIdentity(jobName, defaultGroup)
.withSchedule(CronScheduleBuilder.cronSchedule(cron))
.build();
//使用排程器進行任務排程
try {
scheduler.scheduleJob(jobDetail, cronTrigger);
} catch (SchedulerException e) {
e.printStackTrace();
log.info("建立定時任務失敗!");
}
return jobName;
}
@Override
public String scheduleFixTimeJob(Class<? extends Job> jobBeanClass, Date startTime, String data) {
//日期轉CRON表示式
String startCron = String.format("%d %d %d %d %d ? %d",
DateUtil.second(startTime),
DateUtil.minute(startTime),
DateUtil.hour(startTime, true),
DateUtil.dayOfMonth(startTime),
DateUtil.month(startTime) + 1,
DateUtil.year(startTime));
return scheduleJob(jobBeanClass, startCron, data);
}
@Override
public Boolean cancelScheduleJob(String jobName) {
boolean success = false;
try {
// 暫停觸發器
scheduler.pauseTrigger(new TriggerKey(jobName, defaultGroup));
// 移除觸發器中的任務
scheduler.unscheduleJob(new TriggerKey(jobName, defaultGroup));
// 刪除任務
scheduler.deleteJob(new JobKey(jobName, defaultGroup));
success = true;
} catch (SchedulerException e) {
e.printStackTrace();
}
return success;
}
}
- 定義好需要執行的任務,繼承
QuartzJobBean
類,實現executeInternal
方法即可,這裡定義了三個任務,定時傳送郵件、定時傳送站內信和執行CRON表示式任務;
/**
* 傳送郵件定時任務執行器
* Created by macro on 2020/9/27.
*/
@Slf4j
@Component
public class SendEmailJob extends QuartzJobBean {
@Autowired
private ScheduleService scheduleService;
@Override
protected void executeInternal(JobExecutionContext jobExecutionContext) throws JobExecutionException {
Trigger trigger = jobExecutionContext.getTrigger();
JobDetail jobDetail = jobExecutionContext.getJobDetail();
JobDataMap jobDataMap = jobDetail.getJobDataMap();
String data = jobDataMap.getString("data");
log.info("定時傳送郵件操作:{}",data);
//完成後刪除觸發器和任務
scheduleService.cancelScheduleJob(trigger.getKey().getName());
}
}
/**
* 傳送站內信定時任務執行器
* Created by macro on 2020/9/27.
*/
@Slf4j
@Component
public class SendMessageJob extends QuartzJobBean {
@Autowired
private ScheduleService scheduleService;
@Override
protected void executeInternal(JobExecutionContext jobExecutionContext) throws JobExecutionException {
Trigger trigger = jobExecutionContext.getTrigger();
JobDetail jobDetail = jobExecutionContext.getJobDetail();
JobDataMap jobDataMap = jobDetail.getJobDataMap();
String data = jobDataMap.getString("data");
log.info("定時傳送站內信操作:{}",data);
//完成後刪除觸發器和任務
scheduleService.cancelScheduleJob(trigger.getKey().getName());
}
}
/**
* 使用CRON表示式的任務執行器
* Created by macro on 2020/9/29.
*/
@Slf4j
@Component
public class CronProcessJob extends QuartzJobBean {
@Override
protected void executeInternal(JobExecutionContext jobExecutionContext) throws JobExecutionException {
JobDetail jobDetail = jobExecutionContext.getJobDetail();
JobDataMap jobDataMap = jobDetail.getJobDataMap();
String data = jobDataMap.getString("data");
log.info("CRON表示式任務執行:{}",data);
}
}
- 最後建立好任務排程相關介面,呼叫任務排程業務類即可。
/**
* 定時任務排程相關介面
* Created by macro on 2020/9/29.
*/
@Api(tags = "ScheduleController", description = "定時任務排程相關介面")
@RestController
@RequestMapping("/schedule")
public class ScheduleController {
@Autowired
private ScheduleService scheduleService;
@ApiOperation("定時傳送郵件")
@PostMapping("/sendEmail")
public CommonResult sendEmail(@RequestParam String startTime,@RequestParam String data) {
Date date = DateUtil.parse(startTime, DatePattern.NORM_DATETIME_FORMAT);
String jobName = scheduleService.scheduleFixTimeJob(SendEmailJob.class, date, data);
return CommonResult.success(jobName);
}
@ApiOperation("定時傳送站內信")
@PostMapping("/sendMessage")
public CommonResult sendMessage(@RequestParam String startTime,@RequestParam String data) {
Date date = DateUtil.parse(startTime, DatePattern.NORM_DATETIME_FORMAT);
String jobName = scheduleService.scheduleFixTimeJob(SendMessageJob.class, date, data);
return CommonResult.success(jobName);
}
@ApiOperation("通過CRON表示式排程任務")
@PostMapping("/scheduleJob")
public CommonResult scheduleJob(@RequestParam String cron, @RequestParam String data) {
String jobName = scheduleService.scheduleJob(CronProcessJob.class, cron, data);
return CommonResult.success(jobName);
}
@ApiOperation("取消定時任務")
@PostMapping("/cancelScheduleJob")
public CommonResult cancelScheduleJob(@RequestParam String jobName) {
Boolean success = scheduleService.cancelScheduleJob(jobName);
return CommonResult.success(success);
}
}
執行測試
- 呼叫定時傳送郵件介面測試;
- 到點之後發現控制檯已經列印任務執行資訊;
2020-09-30 11:23:00.035 INFO 10160 --- [eduler_Worker-1] com.macro.mall.tiny.job.SendEmailJob : 定時傳送郵件操作:傳送郵件內容
- 使用CRON表示式來啟動一個定時任務,從0s開始,每隔10s執行一次;
- 控制檯每隔10s列印任務執行資訊;
2020-09-30 11:26:30.024 INFO 10160 --- [eduler_Worker-2] com.macro.mall.tiny.job.CronProcessJob : CRON表示式任務執行:CRON訊息內容
2020-09-30 11:26:40.025 INFO 10160 --- [eduler_Worker-3] com.macro.mall.tiny.job.CronProcessJob : CRON表示式任務執行:CRON訊息內容
2020-09-30 11:26:50.017 INFO 10160 --- [eduler_Worker-4] com.macro.mall.tiny.job.CronProcessJob : CRON表示式任務執行:CRON訊息內容
2020-09-30 11:27:00.023 INFO 10160 --- [eduler_Worker-5] com.macro.mall.tiny.job.CronProcessJob : CRON表示式任務執行:CRON訊息內容
2020-09-30 11:27:10.019 INFO 10160 --- [eduler_Worker-6] com.macro.mall.tiny.job.CronProcessJob : CRON表示式任務執行:CRON訊息內容
- 我們可以通過啟動任務返回的
jobName
,呼叫取消定時任務的介面來取消任務,呼叫成功後定時任務不再執行。
相關文章
- SpringBoot官方支援任務排程框架,輕量級用起來也挺香!Spring Boot框架
- 任務排程
- Airflow 任務排程AI
- Laravel 任務排程Laravel
- SpringBoot整合任務排程框架Quartz及持久化配置Spring Boot框架quartz持久化
- 分散式任務排程分散式
- Spring 指南(排程任務)Spring
- Spark中資源排程和任務排程Spark
- 基於Hyperf開發的任務排程系統.支援任務投遞,DAG任務編排(多個任務使用同一個事務).
- SpringBoot自定義starter開發分散式任務排程實踐Spring Boot分散式
- Timer和TimerTask 任務排程
- 聊聊PowerJob的任務排程
- Linux 定時任務排程Linux
- 分散式排程任務-ElasticJob分散式AST
- LeetCode 621 任務排程器LeetCode
- laravel框架任務排程(定時執行任務)Laravel框架
- Apache DolphinScheduler大規模任務排程系統對大資料實時Flink任務支援Apache大資料
- Python任務排程模組APSchedulerPython
- LTS分散式任務排程部署分散式
- LiteOS-任務篇-原始碼分析-任務排程函式原始碼函式
- Spring排程定時任務的方式Spring
- Android 中的定時任務排程Android
- 力扣-621. 任務排程器力扣
- 任務排程的思考和總結
- Flink - Task 任務排程執行流程
- 力扣 621. 任務排程器力扣
- 任務排程框架Quartz快速入門!框架quartz
- 621. 任務排程器 (構造)
- 進擊的 Kubernetes 排程系統(二):支援批任務的 Coscheduling/Gang scheduling
- Linux 中 Laravel 任務排程不執行LinuxLaravel
- Aloha:一個分散式任務排程框架分散式框架
- 中介軟體---分散式任務排程---Celery分散式
- 任務排程的並行演算法並行演算法
- 使用Java實現定時任務排程Java
- xxl-job,任務排程中心快速上手
- SpringBoot專案接入分散式任務排程平臺xxl-job(2.0.2)說明Spring Boot分散式
- Spring Boot應用中進行任務排程Spring Boot
- micro-job分散式任務排程框架更新分散式框架