SpringBoot集成quartz實(shí)現(xiàn)定時(shí)任務(wù)
1. 前言
現(xiàn)如今,隨著市場(chǎng)競(jìng)爭(zhēng)加劇,各個(gè)企業(yè)都在不斷尋求提高效率、降低成本的方法,此時(shí)使用自動(dòng)化工具已成為必不可少的選擇。而在眾多的自動(dòng)化工具中,定時(shí)任務(wù)已經(jīng)成為一項(xiàng)必備工具,而Quartz就是一個(gè)非常好用的定時(shí)任務(wù)框架,它的輕量級(jí)、高可靠性、易于使用等特點(diǎn),使得它成為一個(gè)非常受歡迎的定時(shí)任務(wù)框架。而在本篇文章中,我們將會(huì)介紹如何使用SpringBoot整合Quartz,并將定時(shí)任務(wù)寫入庫中(持久化存儲(chǔ)),還可以任意對(duì)定時(shí)任務(wù)進(jìn)行如刪除、暫停、恢復(fù)等操作。
那么,具體如何實(shí)現(xiàn)呢?這將又會(huì)是干貨滿滿的一期,全程無尿點(diǎn)不廢話只抓重點(diǎn)教,具有非常好的學(xué)習(xí)效果,拿好小板凳準(zhǔn)備就坐!希望學(xué)習(xí)的過程中大家認(rèn)真聽好好學(xué),學(xué)習(xí)的途中有任何不清楚或疑問的地方皆可評(píng)論區(qū)留言或私信,bug菌將第一時(shí)間給予解惑,那么廢話不多說,直接開整!Fighting??!
2. 環(huán)境說明
本地的開發(fā)環(huán)境:
- 開發(fā)工具:IDEA 2021.3
- JDK版本: JDK 1.8
- Spring Boot版本:2.3.1 RELEASE
- Maven版本:3.8.2
3. 整合Quartz
下面實(shí)現(xiàn)一個(gè)簡單的任務(wù)調(diào)度系統(tǒng),并且將定時(shí)任務(wù)的信息持久化入庫,希望可以幫助到大家,Quartz的非持久化使用本文就不贅述了。
3.1 搭建Spring Boot應(yīng)用
首先,我們需要搭建一個(gè)Spring Boot項(xiàng)目,如果還不會(huì)點(diǎn)這里,此處就不詳細(xì)贅述啦。
3.2 添加依賴
在Spring Boot項(xiàng)目中,我們可以使用Quartz Starter來集成Quartz框架。只需要在pom.xml文件中引入以下依賴即可:
<!--集成quartz--> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-quartz</artifactId> <version>2.3.12.RELEASE</version> </dependency>
基礎(chǔ)相關(guān)依賴:
<dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> </dependency> <!--mysql依賴--> <dependency> <groupId>mysql</groupId> <artifactId>mysql-connector-java</artifactId> </dependency> <dependency> <groupId>com.alibaba</groupId> <artifactId>druid-spring-boot-starter</artifactId> <version>1.1.10</version> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-jdbc</artifactId> </dependency> <!--util工具類--> <dependency> <groupId>org.apache.commons</groupId> <artifactId>commons-lang3</artifactId> <version>3.8.1</version> </dependency>
3.3 配置Quartz相關(guān)配置文件
然后,我們需要在application.properties文件中配置數(shù)據(jù)源及Quartz相關(guān)配置:
spring: datasource: name: db_quartz driver-class-name: com.mysql.cj.jdbc.Driver url: jdbc:mysql://localhost:3306/db_quartz?useSSL=false&serverTimezone=Asia/Shanghai&characterEncoding=UTF8&rewriteBatchedStatements=true username: root password: 123456 #quartz配置 quartz: #數(shù)據(jù)存儲(chǔ)方式 默認(rèn)是 MEMORY(內(nèi)存方式) job-store-type: jdbc properties: org: quartz: scheduler: #調(diào)度標(biāo)識(shí)名 instanceName: clusteredScheduler #ID設(shè)置為自動(dòng)獲取 每一個(gè)必須不同 instanceId: AUTO jobStore: #數(shù)據(jù)保存方式為持久化 class: org.quartz.impl.jdbcjobstore.JobStoreTX #數(shù)據(jù)庫平臺(tái) driverDelegateClass: org.quartz.impl.jdbcjobstore.StdJDBCDelegate #表的前綴 tablePrefix: QRTZ_ #加入集群 isClustered: false #調(diào)度實(shí)例失效的檢查時(shí)間間隔 clusterCheckinInterval: 10000 #設(shè)置為TRUE不會(huì)出現(xiàn)序列化非字符串類到 BLOB 時(shí)產(chǎn)生的類版本問題 useProperties: false threadPool: #ThreadPool 實(shí)現(xiàn)的類名 class: org.quartz.simpl.SimpleThreadPool #線程數(shù)量 threadCount: 5 #線程優(yōu)先級(jí) threadPriority: 5 #自創(chuàng)建父線程 threadsInheritContextClassLoaderOfInitializingThread: true
其中,job-store-type表示使用的持久化方式,這里選擇了使用jdbc完成任務(wù)持久化入庫。jdbc的相關(guān)配置包括initialize-schema,comment-prefix,use-properties,table-prefix,scheduler-name等,這里不再贅述,具體的我已在代碼中進(jìn)行了詳細(xì)注釋。
3.4 添加Quartz表
接下來,我們需要?jiǎng)?chuàng)建一個(gè)數(shù)據(jù)庫表,用于存儲(chǔ)任務(wù)信息。在使用quartz做持久化的時(shí)候需要用到quartz的11張表,可以去quartz官網(wǎng)下載對(duì)應(yīng)版本的quartz,你進(jìn)入quartz的官網(wǎng)Quartz Enterprise Job Scheduler,點(diǎn)擊Downloads,若想要現(xiàn)成的,可以去我的github倉庫取https://github.com/luoyong0603/SpringBoot-demos。下載并解壓,打開docs/dbTables里面有對(duì)應(yīng)數(shù)據(jù)庫的建表語句。演示截圖如下:
我這選擇的是mysql數(shù)據(jù)庫且使用innodb引擎,對(duì)應(yīng)的腳本文件是tables_mysql_innodb.sql,這里大家可以自行去了解下innodb與myisam的區(qū)別。這些表不需要我們做任何操作,是Quartz框架使用的。把創(chuàng)表sql執(zhí)行后,就如下這樣了,總共11張表,示例如下:
接下來對(duì)每張表的作用做以下總結(jié),Quartz將Job保存在數(shù)據(jù)庫中所需表的說明:
- QRTZ_CALENDARS:以 Blob 類型存儲(chǔ) Quartz 的 Calendar 信息
- QRTZ_CRON_TRIGGERS:存儲(chǔ) Cron Trigger,包括 Cron表達(dá)式和時(shí)區(qū)信息
- QRTZ_FIRED_TRIGGERS:存儲(chǔ)與已觸發(fā)的 Trigger 相關(guān)的狀態(tài)信息,以及相聯(lián) Job的執(zhí)行信息
- QRTZ_PAUSED_TRIGGER_GRPS:存儲(chǔ)已暫停的 Trigger 組的信息
- QRTZ_SCHEDULER_STATE 存儲(chǔ)少量的有關(guān) Scheduler 的狀態(tài)信息,和別的 Scheduler實(shí)例(假如是用于一個(gè)集群中)
- QRTZ_LOCKS 存儲(chǔ)程序的悲觀鎖的信息(假如使用了悲觀鎖)
- QRTZ_JOB_DETAILS 存儲(chǔ)每一個(gè)已配置的 Job 的詳細(xì)信息
- QRTZ_SIMPLE_TRIGGERS 存儲(chǔ)簡單的Trigger,包括重復(fù)次數(shù),間隔,以及已觸的次數(shù)
- QRTZ_BLOG_TRIGGERS Trigger 作為 Blob 類型存儲(chǔ)(用于 Quartz 用戶用 JDBC創(chuàng)建他們自己定制的 Trigger 類型,JobStore 并不知道如何存儲(chǔ)實(shí)例的時(shí)候)
- QRTZ_TRIGGER_LISTENERS 存儲(chǔ)已配置的 TriggerListener 的信息
- QRTZ_TRIGGERS 存儲(chǔ)已配置的 Trigger 的信息
并對(duì)其中的4張常用表進(jìn)行表結(jié)構(gòu)解讀,輔助大家理解:
表qrtz_job_details: 保存job詳細(xì)信息,該表需要用戶根據(jù)實(shí)際情況初始化
job_name:集群中job的名字,該名字用戶自己可以隨意定制,無強(qiáng)行要求
job_group:集群中job的所屬組的名字,該名字用戶自己隨意定制,無強(qiáng)行要求
job_class_name:集群中個(gè)note job實(shí)現(xiàn)類的完全包名,quartz就是根據(jù)這個(gè)路徑到classpath找到該job類
is_durable:是否持久化,把該屬性設(shè)置為1,quartz會(huì)把job持久化到數(shù)據(jù)庫中
job_data:一個(gè)blob字段,存放持久化job對(duì)象
表qrtz_triggers: 保存trigger信息
trigger_name: trigger的名字,該名字用戶自己可以隨意定制,無強(qiáng)行要求
trigger_group:trigger所屬組的名字,該名字用戶自己隨意定制,無強(qiáng)行要求
job_name: qrtz_job_details表job_name的外鍵
job_group: qrtz_job_details表job_group的外鍵
trigger_state:當(dāng)前trigger狀態(tài),設(shè)置為ACQUIRED,如果設(shè)置為WAITING,則job不會(huì)觸發(fā)
trigger_cron:觸發(fā)器類型,使用cron表達(dá)式
表qrtz_cron_triggers:存儲(chǔ)cron表達(dá)式表
trigger_name: qrtz_triggers表trigger_name的外鍵
trigger_group: qrtz_triggers表trigger_group的外鍵
cron_expression:cron表達(dá)式
表qrtz_scheduler_state:存儲(chǔ)集群中note實(shí)例信息,quartz會(huì)定時(shí)讀取該表的信息判斷集群中每個(gè)實(shí)例的當(dāng)前狀態(tài)
instance_name:之前配置文件中org.quartz.scheduler.instanceId配置的名字,就會(huì)寫入該字段,如果設(shè)置為AUTO,quartz會(huì)根據(jù)物理機(jī)名和當(dāng)前時(shí)間產(chǎn)生一個(gè)名字
last_checkin_time:上次檢查時(shí)間
checkin_interval:檢查間隔時(shí)間
3.5 創(chuàng)建Controller
這里我們創(chuàng)建個(gè)QuartzController控制器,用來添加多個(gè)操作Quartz的請(qǐng)求方式,例如:刪除定時(shí)任務(wù)、暫停定時(shí)任務(wù)、查看所有定時(shí)任務(wù)等。Swagger示例截圖如下:
示例代碼如下:僅供參考,有想法的也可以拓展或者修改。
@RestController @RequestMapping("/quartz") public class QuartzController { @Autowired private QuartzService quartzService; /** * 新增任務(wù) */ @PostMapping("/insert") @ApiOperation(value = "新增任務(wù)", notes = "新增任務(wù)") public Boolean insertTask(@RequestBody QuartzModel quartzModel) { return quartzService.addJob(quartzModel); } /** * 暫停任務(wù) */ @PostMapping("/pause") @ApiOperation(value = "暫停任務(wù)", notes = "暫停任務(wù)") public Boolean pauseTask(@RequestBody OperationModel operationModel) { return quartzService.pauseJob(operationModel); } /** * 恢復(fù)任務(wù) */ @PostMapping("/resume") @ApiOperation(value = "恢復(fù)任務(wù)", notes = "恢復(fù)任務(wù)") public Boolean resumeTask(@RequestBody OperationModel operationModel) { return quartzService.resumeJob(operationModel); } /** * 刪除任務(wù) */ @PostMapping("/delete") @ApiOperation(value = "刪除任務(wù)", notes = "刪除任務(wù)") public Boolean deleteTask(@RequestBody OperationModel operationModel) { return quartzService.deleteJob(operationModel); } /** * 查看所有定時(shí)任務(wù) */ @GetMapping("/query-all-job") @ApiOperation(value = "查看所有定時(shí)任務(wù)", notes = "查看所有定時(shí)任務(wù)") public List<Map<String, Object>> queryAllJob() { return quartzService.queryAllJob(); } /** * 查看正在運(yùn)行的任務(wù) */ @GetMapping("/query-all-running-job") @ApiOperation(value = "查看正在執(zhí)行的任務(wù)", notes = "查看正在執(zhí)行的任務(wù)") public List<Map<String, Object>> queryAllRunningJob() { return quartzService.queryAllRunningJob(); } }
3.6 編寫QuartzService接口
這里將被定義所對(duì)應(yīng)的接口,用于操作Quartz任務(wù)。
@Service public interface QuartzService { /** * 新增定時(shí)任務(wù) */ Boolean addJob(QuartzModel quartzModel); /** * 暫停定時(shí)任務(wù) */ Boolean pauseJob(OperationModel operationModel); /** * 繼續(xù)定時(shí)任務(wù) */ Boolean resumeJob(OperationModel operationModel); /** * 刪除定時(shí)任務(wù) */ Boolean deleteJob(OperationModel operationModel); /** * 查詢所有計(jì)劃中的任務(wù)列表 */ List<Map<String, Object>> queryAllJob(); /** * 查看正在運(yùn)行的任務(wù) */ List<Map<String, Object>> queryAllRunningJob(); }
3.7 實(shí)現(xiàn)定時(shí)任務(wù)執(zhí)行器
這里我們手動(dòng)寫任務(wù)執(zhí)行器,目的不僅是為了執(zhí)行定時(shí)任務(wù)也想通過區(qū)分定時(shí)任務(wù)執(zhí)行不同的邏輯,示例代碼如下,僅供參考:
/** * 定時(shí)任務(wù)執(zhí)行器 */ @Slf4j public class TaskDistributor extends QuartzJobBean { /** * 根據(jù)不同的jobDetail攜帶的type參數(shù)區(qū)分,執(zhí)行不同業(yè)務(wù) */ @Override protected void executeInternal(JobExecutionContext context) { synchronized (this) { log.info("開始執(zhí)行定時(shí)任務(wù)..............."); try { //獲取job中攜帶的內(nèi)容并轉(zhuǎn)成jobDetailModel對(duì)象 JobDetailModel jobDetailModel = (JobDetailModel) context.getJobDetail().getJobDataMap().get("jobDetailModel"); Integer type = jobDetailModel.getType(); String content = jobDetailModel.getContent(); //根據(jù)type執(zhí)行不同的業(yè)務(wù)邏輯 switch (type) { case 1: System.out.println("task1即將被執(zhí)行,content:" + content); //模擬處理邏輯 Thread.sleep(5000); log.info("---------task1定時(shí)任務(wù)執(zhí)行結(jié)束----------"); break; case 2: System.out.println("task2即將被執(zhí)行,content:" + content); //模擬處理邏輯 Thread.sleep(5000); log.info("----------task2定時(shí)任務(wù)結(jié)束---------"); break; default: log.info("---------定時(shí)任務(wù)執(zhí)行開始----------"); log.info("---------定時(shí)任務(wù)執(zhí)行結(jié)束----------"); break; } } catch (Throwable t) { log.error(t.getMessage(), t); } log.info("定時(shí)任務(wù)執(zhí)行結(jié)束..............."); } } }
3.8 創(chuàng)建QuartzServiceImpl實(shí)現(xiàn)類
這里我們將QuartzService接口中的所有接口進(jìn)行實(shí)現(xiàn),示例代碼如下:
@Slf4j @Service public class QuartzServiceImpl implements QuartzService { @Autowired private Scheduler scheduler; /** * 新增定時(shí)任務(wù) */ @Override public Boolean addJob(QuartzModel quartzModel) { try { String cron = quartzModel.getCron(); //校驗(yàn)cron是否有效 boolean valid = CronUtils.isValid(cron); if (!valid) { return false; } JobDetail jobDetail = JobBuilder.newJob(TaskDistributor.class) .withIdentity(quartzModel.getJobName(), quartzModel.getJobGroup()) .build(); // 攜帶job內(nèi)容 此處可以攜帶業(yè)務(wù)所需要的執(zhí)行參數(shù) JobDetailModel jobDetailModel = quartzModel.getJobData(); if (!Objects.isNull(jobDetailModel)) { jobDetail.getJobDataMap().put("jobDetailModel", jobDetailModel); } CronTrigger trigger = TriggerBuilder.newTrigger() .withIdentity(quartzModel.getTriggerName(), quartzModel.getTriggerGroup()) .startNow() .withSchedule(CronScheduleBuilder.cronSchedule(cron)) .build(); //把作業(yè)和觸發(fā)器注冊(cè)到任務(wù)調(diào)度中 scheduler.scheduleJob(jobDetail, trigger); //啟用調(diào)度 scheduler.start(); log.info("---------定時(shí)任務(wù)成功添加進(jìn)quartz隊(duì)列中!----------"); } catch (Exception e) { e.printStackTrace(); } return true; } /** * 暫停定時(shí)任務(wù) */ @Override public Boolean pauseJob(OperationModel operationModel) { try { scheduler.pauseJob(JobKey.jobKey(operationModel.getJobName(), operationModel.getJobGroup())); System.out.println("暫停定時(shí)任務(wù)成功"); } catch (Exception e) { e.printStackTrace(); } return true; } /** * 繼續(xù)定時(shí)任務(wù) */ @Override public Boolean resumeJob(OperationModel operationModel) { try { scheduler.resumeJob(JobKey.jobKey(operationModel.getJobName(), operationModel.getJobGroup())); System.out.println("恢復(fù)定時(shí)任務(wù)成功"); } catch (SchedulerException e) { e.printStackTrace(); } return true; } /** * 刪除定時(shí)任務(wù) */ @Override public Boolean deleteJob(OperationModel operationModel) { try { // TriggerKey 定義了trigger的名稱和組別 ,通過任務(wù)名和任務(wù)組名獲取TriggerKey TriggerKey triggerKey = TriggerKey.triggerKey(operationModel.getJobName(), operationModel.getJobGroup()); // 停止觸發(fā)器 scheduler.resumeTrigger(triggerKey); // 移除觸發(fā)器 scheduler.unscheduleJob(triggerKey); scheduler.deleteJob(JobKey.jobKey(operationModel.getJobName(), operationModel.getJobGroup())); System.out.println("刪除定時(shí)任務(wù)成功"); } catch (SchedulerException e) { e.printStackTrace(); } return true; } /** * 查看所有定時(shí)任務(wù) * * @return */ @Override public List<Map<String, Object>> queryAllJob() { List<Map<String, Object>> jobList = null; try { GroupMatcher<JobKey> matcher = GroupMatcher.anyJobGroup(); Set<JobKey> jobKeys = scheduler.getJobKeys(matcher); jobList = new ArrayList<Map<String, Object>>(); for (JobKey jobKey : jobKeys) { List<? extends Trigger> triggers = scheduler.getTriggersOfJob(jobKey); for (Trigger trigger : triggers) { jobList.add(this.buildMap(jobKey, trigger)); } } } catch (SchedulerException e) { e.printStackTrace(); } return jobList; } /** * 返回指定的map集合 */ private Map<String, Object> buildMap(JobKey jobKey, Trigger trigger) throws SchedulerException { Map<String, Object> map = new HashMap<>(); map.put("jobName", jobKey.getName()); map.put("jobGroupName", jobKey.getGroup()); map.put("description", "觸發(fā)器:" + trigger.getKey()); Trigger.TriggerState triggerState = scheduler.getTriggerState(trigger.getKey()); map.put("jobStatus", triggerState.name()); if (trigger instanceof CronTrigger) { CronTrigger cronTrigger = (CronTrigger) trigger; String cronExpression = cronTrigger.getCronExpression(); map.put("jobTime", cronExpression); } return map; } /** * 獲取所有正在運(yùn)行的job * * @return */ @Override public List<Map<String, Object>> queryAllRunningJob() { List<Map<String, Object>> jobList = null; try { List<JobExecutionContext> executingJobs = scheduler.getCurrentlyExecutingJobs(); jobList = new ArrayList<Map<String, Object>>(executingJobs.size()); for (JobExecutionContext executingJob : executingJobs) { JobDetail jobDetail = executingJob.getJobDetail(); JobKey jobKey = jobDetail.getKey(); Trigger trigger = executingJob.getTrigger(); jobList.add(this.buildMap(jobKey, trigger)); } } catch (SchedulerException e) { e.printStackTrace(); } return jobList; } }
其中addJob方法將一個(gè)定時(shí)任務(wù)添加到調(diào)度器中,其中jobName和jobGroupName表示任務(wù)的名稱和分組名稱,triggerName和triggerGroupName表示觸發(fā)器的名稱和分組名稱,jobClass表示任務(wù)的類,cron表示定時(shí)任務(wù)執(zhí)行的時(shí)間表達(dá)式。scheduler.scheduleJob(jobDetail, trigger);目的是把作業(yè)和觸發(fā)器注冊(cè)到任務(wù)調(diào)度中。
3.9 接口測(cè)試
重啟項(xiàng)目后,我們來進(jìn)行接口的請(qǐng)求測(cè)試,驗(yàn)證我們所添加的定時(shí)任務(wù)是否會(huì)執(zhí)行且被新增入庫持久化保存?就讓我們拭目以待吧!
如下我直接通過Swagger在線接口文檔作為測(cè)試,測(cè)試如下:
3.9.1 添加定時(shí)任務(wù)task1
具體請(qǐng)求參數(shù)附上:
{
"cron": "*/10 * * * * ? ",
"jobData": {
"content": "bug菌寫定時(shí)任務(wù)(執(zhí)行邏輯1)",
"type": 1
},
"jobGroup": "job_group_task1",
"jobName": "job_name_task1",
"triggerGroup": "trigger_group_task1",
"triggerName": "trigger_task1"
}
3.9.2 添加定時(shí)任務(wù)task2
具體請(qǐng)求參數(shù)附上:
{
"cron": "*/20 * * * * ? ",
"jobData": {
"content": "bug菌寫定時(shí)任務(wù)(執(zhí)行邏輯2)",
"type": 2
},
"jobGroup": "job_group_task2",
"jobName": "job_name_task2",
"triggerGroup": "trigger_group_task2",
"triggerName": "trigger_task2"
}
3.9.3 測(cè)試獲取所有的定時(shí)任務(wù)
添加兩個(gè)定時(shí)任務(wù)后,可以查看一下所有定時(shí)任務(wù)列表:
定時(shí)任務(wù)成功添加進(jìn)quartz隊(duì)列中!被插入的定時(shí)任務(wù)task1被成功執(zhí)行且執(zhí)行的type = 1的業(yè)務(wù)邏輯,打印了" task1即將被執(zhí)行,content:bug菌寫定時(shí)任務(wù)(執(zhí)行邏輯1) "
示例截圖如下:
定時(shí)任務(wù)成功添加進(jìn)quartz隊(duì)列中!被插入的定時(shí)任務(wù)task2被成功執(zhí)行且執(zhí)行的type = 2的業(yè)務(wù)邏輯,打印了" task2即將被執(zhí)行,content:bug菌寫定時(shí)任務(wù)(執(zhí)行邏輯2) "
示例截圖如下:
3.9.4 驗(yàn)證數(shù)據(jù)庫是否被保存定時(shí)任務(wù)
我們?cè)賮頇z查一遍,查驗(yàn)下我們添加的兩條定時(shí)任務(wù)是否被自動(dòng)插入庫中(持久化)。
在上面的兩個(gè)添加任務(wù)的過程中,我們不僅將定時(shí)任務(wù)配置在了代碼中,并把將它們持久化到數(shù)據(jù)庫中。因?yàn)槲覀兙褪窍M趹?yīng)用程序重啟后仍然能夠保持任務(wù)狀態(tài),而Quartz就在內(nèi)部直接將它們持久化到數(shù)據(jù)庫中。
在Quartz中,定時(shí)任務(wù)數(shù)據(jù)是存儲(chǔ)在三張表中的:
- QRTZ_JOB_DETAILS:保存JobDetail信息
- QRTZ_TRIGGERS:保存Trigger信息
- QRTZ_CRON_TRIGGERS:保存CronTrigger信息
當(dāng)我們插入定時(shí)任務(wù)時(shí),Quartz會(huì)自動(dòng)將任務(wù)數(shù)據(jù)存儲(chǔ)到這三張表中。我們插入了兩個(gè)task定時(shí)任務(wù)后,我們完全不需要自主手插入定時(shí)任務(wù),它會(huì)自動(dòng)幫我們保存入庫,同學(xué)們請(qǐng)看:
qrtz_cron_triggers表:
qrtz_job_details表:
qrtz_triggers表:
這樣一來,哪怕是項(xiàng)目重啟,定時(shí)任務(wù)也照常執(zhí)行,不會(huì)因?yàn)闆]有被觸發(fā)而停止執(zhí)行定時(shí)任務(wù)??梢钥吹皆俅沃貑⒑蟮捻?xiàng)目task1與task2都自動(dòng)執(zhí)行,保持了任務(wù)狀態(tài)。
3.9.5 暫停定時(shí)任務(wù)
具體請(qǐng)求參數(shù)附上:
{
"jobGroup": "job_group_task1_jg",
"jobName": "job_name_task1_jn"
}
切記,我是為了區(qū)分便在參數(shù)pojo值上手動(dòng)拼接后綴,比如:
可以看到數(shù)據(jù)入庫的都被添加了對(duì)應(yīng)的后綴名。
所以同學(xué)們?cè)跍y(cè)試接口的時(shí)候,別忘了把后綴加上,或者你們也可以把我的這段去掉。
控制臺(tái)查驗(yàn)是否被暫停了,大家請(qǐng)看:
經(jīng)多次測(cè)試,task1任務(wù)確實(shí)暫停執(zhí)行了。
3.9.6 繼續(xù)定時(shí)任務(wù)
我們能暫停任務(wù),就能把被暫停的任務(wù)恢復(fù)執(zhí)行,這么我們來測(cè)試一下。
具體請(qǐng)求參數(shù)附上:
{
"jobGroup": "job_group_task1_jg",
"jobName": "job_name_task1_jn"
}
控制臺(tái)查驗(yàn)被暫停的task1是否被恢復(fù)執(zhí)行了,大家請(qǐng)看:
經(jīng)5min觀察,task1任務(wù)確實(shí)又重新執(zhí)行了。你也可以調(diào)用【正在執(zhí)行中的任務(wù)】接口作為輔助驗(yàn)證,不過就是要多次調(diào)用,畢竟在非執(zhí)行間隔有點(diǎn)久,畢竟task1任務(wù)間隔10s執(zhí)行一次,task2任務(wù)間隔20s執(zhí)行一次。
3.9.7 查詢正在執(zhí)行的定時(shí)任務(wù)
同上述測(cè)試步驟,我們直接通過Swagger接口文檔作為測(cè)試請(qǐng)求:
再次測(cè)試請(qǐng)求:
3.9.8 查看所有定時(shí)任務(wù)
這里我們可以查詢出所有的定時(shí)任務(wù),不區(qū)分是否正在被執(zhí)行狀態(tài)。所以始終會(huì)返回job表中的總條數(shù)。
同上述測(cè)試步驟,我們直接通過Swagger接口文檔作為測(cè)試請(qǐng)求:
3.9.9 刪除定時(shí)任務(wù)
終于寫到最后一個(gè)刪除模擬了,同上述測(cè)試步驟,我們直接通過Swagger接口文檔作為測(cè)試請(qǐng)求:
具體請(qǐng)求參數(shù)附上:
{
"jobGroup": "job_group_task1_jg",
"jobName": "job_name_task1_jn"
}
我們從三個(gè)地方來驗(yàn)證,分別是接口查詢所有任務(wù)數(shù),控制臺(tái)執(zhí)行打印,及數(shù)據(jù)庫job任務(wù)條數(shù)。
重新請(qǐng)求【接口查詢所有定時(shí)任務(wù)數(shù)】截圖如下:
數(shù)據(jù)庫表qrtz_job_details中task1成功被移除了,證明task1這條定時(shí)任務(wù)確實(shí)被刪除了。
好啦,以上針對(duì)Quartz的一系列操作及測(cè)試演示了,所任何不清楚的地方都可以通過評(píng)論區(qū)或私信我,我將第一時(shí)間給予你解惑。
4. 總結(jié)
本文介紹了SpringBoot集成quartz可以使定時(shí)任務(wù)的管理變得更加方便和可靠。通過將定時(shí)任務(wù)的信息保存到數(shù)據(jù)庫中,我們可以輕松地對(duì)任務(wù)進(jìn)行增刪改查等操作。下面是一些主要的總結(jié):
配置數(shù)據(jù)源和quartz的相關(guān)屬性:需要在配置文件中指定數(shù)據(jù)源的連接信息和quartz的屬性,包括JobStore類型、表前綴、是否自動(dòng)啟動(dòng)等。
創(chuàng)建Job和Trigger:通過繼承Quartz提供的Job接口,實(shí)現(xiàn)execute方法,即可定義一個(gè)Job。同時(shí),通過指定Trigger的cron表達(dá)式或簡單的時(shí)間間隔,可以定義一個(gè)Trigger,并將其與Job綁定起來。
通過schedulerFactoryBean創(chuàng)建Scheduler:在配置類中,通過注入schedulerFactoryBean,我們可以獲取到一個(gè)Scheduler實(shí)例,然后可以通過Scheduler的API實(shí)現(xiàn)任務(wù)的管理,包括啟動(dòng)、暫停、恢復(fù)、刪除等操作。
配置JobDetail、Trigger、CronTrigger的持久化:通過將JobDetail、Trigger、CronTrigger的信息保存到數(shù)據(jù)庫中,我們可以實(shí)現(xiàn)對(duì)定時(shí)任務(wù)的持久化,即使應(yīng)用程序重新啟動(dòng)或服務(wù)器崩潰,我們也可以恢復(fù)定時(shí)任務(wù)。在配置文件中,需要指定SchedulerFactoryBean的jobStore類型為JobStoreTX,同時(shí)配置DataSourceTransactionManager和QuartzTransactionManager。
使用QuartzJobBean和JobDataMap:在Job中,可以通過繼承Quartz提供的QuartzJobBean,獲取JobExecutionContext,并從中獲取JobDataMap,通過JobDataMap可以傳遞參數(shù),實(shí)現(xiàn)Job的可配置化。
總之,通過使用SpringBoot集成quartz,我們可以方便地管理定時(shí)任務(wù),實(shí)現(xiàn)任務(wù)的持久化,同時(shí)對(duì)任務(wù)的管理變得更加靈活和可靠。
以上就是SpringBoot集成quartz實(shí)現(xiàn)定時(shí)任務(wù)的詳細(xì)內(nèi)容,更多關(guān)于SpringBoot定時(shí)任務(wù)的資料請(qǐng)關(guān)注腳本之家其它相關(guān)文章!
- 如何對(duì)quartz定時(shí)任務(wù)設(shè)置結(jié)束時(shí)間
- SpringBoot中使用Quartz設(shè)置定時(shí)任務(wù)的實(shí)例詳解
- Springboot集成Quartz實(shí)現(xiàn)定時(shí)任務(wù)代碼實(shí)例
- Springboot整合quartz實(shí)現(xiàn)多個(gè)定時(shí)任務(wù)實(shí)例
- SpringBoot+Quartz實(shí)現(xiàn)定時(shí)任務(wù)的代碼模版分享
- Quartz定時(shí)任務(wù)管理方式(動(dòng)態(tài)添加、停止、恢復(fù)、刪除定時(shí)任務(wù))
相關(guān)文章
SpringBoot中整合knife4j接口文檔的實(shí)踐
這篇文章主要介紹了SpringBoot中整合knife4j接口文檔的實(shí)踐,文中通過示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧2020-09-09Java中數(shù)學(xué)相關(guān)類的使用教程
Java是一種廣泛使用的編程語言,它提供了許多數(shù)學(xué)運(yùn)算的函數(shù)和方法,使得開發(fā)者可以輕松地進(jìn)行各種數(shù)學(xué)計(jì)算,下面這篇文章主要給大家介紹了關(guān)于Java中數(shù)學(xué)相關(guān)類使用的相關(guān)資料,文中通過實(shí)例代碼介紹的非常詳細(xì),需要的朋友可以參考下2023-05-05Java基礎(chǔ)之隱式轉(zhuǎn)換vs強(qiáng)制轉(zhuǎn)換
這篇文章主要介紹了Java基礎(chǔ)之隱式轉(zhuǎn)換vs強(qiáng)制轉(zhuǎn)換的相關(guān)資料,需要的朋友可以參考下2015-12-12Java中LambdaQueryWrapper的常用方法詳解
這篇文章主要給大家介紹了關(guān)于Java中LambdaQueryWrapper常用方法的相關(guān)資料,lambdaquerywrapper是一個(gè)Java庫,用于構(gòu)建類型安全的Lambda表達(dá)式查詢,需要的朋友可以參考下2023-11-11