文章目錄
前言
新手向筆記,來源多爲網上博客例子。
SpringBoot整合Quartz簡單完整例子
SpringBoot整合Quartz作爲調度中心完整實用例子
spring-boot-2.0.3之quartz集成,不是你想的那樣哦!
精通SpringBoot——第十篇:使用Quartz實現動態配置定時任務
quartz初次使用踩坑記
Quartz 框架
- 在現實開發中,我們常常會遇到需要系統在特定時刻完成特定任務的需求,通過引介增強來簡單地模擬實現了一個定時器。它可能只需要我們自己維護一條線程就足以實現定時監控。
但在實際開發中,我們遇到的需求會複雜很多,可能涉及多點任務調度,需要我們多線程併發協作、線程池的維護、對運行時間規則進行更細粒度的規劃、運行線程現場的保持與恢復等等。如果我們選擇自己來造輪子,可能會遇到許多難題。這時候,引入Quartz任務調度框架會是一個很好的選擇,它:
- 允許我們靈活而細粒度地設置任務觸發時間,比如常見的每隔多長時間,或每天特定時刻、特定日子(如節假日),都能靈活地配置這些成相應時間點來觸發我們的任務
- 提供了調度環境的持久化機制,可以將任務內容保存到數據庫中,在完成後刪除記錄,這樣就能避免因系統故障而任務尚未執行且不再執行的問題。
- 提供了組件式的偵聽器、各種插件、線程池等。通過偵聽器,我們可以引入我們的事件機制,配合上異步調用,既能爲我們的業務處理類解耦,同時還可能提升用戶體驗
Quartz API
- 基本要素:
Scheduler:任務調度器。所有的調度都是由它控制
Job:由希望由調度程序執行的組件實現的接口
JobDetail:用於定義任務的實例,如任務名稱
Trigger(即觸發器):定義觸發的條件。如,每隔1秒執行一次 - JobBuilder:用於定義/構建JobDetail實例,用於定義作業的實例
TriggerBuilder :用於定義/構建觸發器實例
Quartz 數據庫表
注意:cron方式需要用到的4張數據表:
qrtz_job_details;qrtz_triggers;qrtz_cron_triggers;qrtz_fired_triggers
-
QRTZ_JOB_DETAILS
存儲每一個已配置的任務Job的詳細信息 -
QRTZ_TRIGGERS
存儲已配置的觸發器Trigger的信息 -
QRTZ_CRON_TRIGGERS
存儲Cron Trigger,包括Cron表達式和時區信息 -
QRTZ_FIRED_TRIGGERS
存儲每個正在執行的觸發器Trigger相關的狀態信息,以及相聯Job的執行信息 -
QRTZ_SCHEDULER_STATE
記錄 調度器(每個機器節點)的生命狀態 -
QRTZ_LOCKS
記錄程序的悲觀鎖(防止多個節點同時執行同一個定時任務) -
QRTZ_SIMPLE_TRIGGERS
存儲已配置的Simple Trigger的信息 -
QRTZ_SIMPROP_TRIGGERS
存儲CalendarIntervalTrigger和DailyTimeIntervalTrigger兩種類型的觸發器 -
QRTZ_BLOB_TRIGGERS
Trigger作爲Blob類型存儲(用於Quartz用戶用JDBC創建他們自己定製的Trigger類型,JobStore並不知道如何存儲實例的時候) -
QRTZ_CALENDARS
以Blob類型存儲Quartz的Calendar日曆信息,quartz可配置一個日曆來指定一個時間範圍 -
QRTZ_PAUSED_TRIGGER_GRPS
存儲已暫停的Trigger組的信息
搭建任務調度工程的簡單流程
1. 創建 Job 類
描述任務要執行的具體內容,任務要做什麼,比如打印輸出文字等等
注:Job類必須單獨一個文件,必須爲public
import org.quartz.Job;
import org.quartz.JobExecutionContext;
import org.quartz.JobExecutionException;
/**
* 任務執行的內容
* 注:必須單獨一個文件
* 注:必須爲public
**/
public class MyJob1 implements Job {
@Override
public void execute(JobExecutionContext context) throws JobExecutionException {
System.out.println("...abc打印123456...");
}
}
2. 創建 JobDetail
任務的信息,如名稱、組別、描述等
// 2. 創建 JobDetail
JobDetail jobDetail =JobBuilder.newJob(MyJob1.class)
.withIdentity("job1", "group1")
.withDescription("MyJob1Detail des.")
.build();
3. 創建觸發器 Trigger
觸發器的信息,如名稱、組別、描述等
觸發器開始執行的時間、週期、間隔等;
用 TriggerBuilder.newTrigger() 建立;接着定義各種屬性;
SimpleTrigger
普通的觸發器
// 3. 創建觸發器Trigger
Trigger trigger =
TriggerBuilder.newTrigger()
.withIdentity("trigger1", "group")
.withDescription("trigger1 des.")
// .startNow()
.startAt(DateBuilder.dateOf(10, 00, 00))
.endAt(DateBuildr.dateOf(11,00,00))
.withSchedule(SimpleScheduleBuilder
.simpleSchedule()
// 間隔
.withIntervalInSeconds(1)
// 重複次數,7次
// .repeatForever()
.withRepeatCount(7)
// 若失效,之後再恢復並馬上執行
.withMisfireHandlingInstructionFireNow()
)
.forJob("job1", "group1")
.build();
- 使用.forJob(“job1”, “group1”):IDEA會顯示 JobName、JobGruop 灰色的信息提示字樣
CronTrigger
使用基於日曆的觸發機制的觸發器,cron表達式
// 使用基於日曆的觸發機制的觸發器 CronTrigger
String cron1 = "* * * * * ? *";
CronTrigger cronTrigger = (CronTrigger)TriggerBuilder.newTrigger()
.withIdentity("cron trigger 1", "group")
.withDescription("cron trigger 1 des.")
.startNow()
.withSchedule(CronScheduleBuilder
.cronSchedule(cron1)
)
.build();
4. 創建調度器 Scheduler
調度器的創建模式:一般用 工廠模式 SchedulerFactory 創建
調度器所要調度的 任務的信息(jobDetail)、所要調度的 觸發器(trigger)
開始執行任務: scheduler.start();
// 4. 創建調度器 Scheduler
SchedulerFactory schedulerFactory = new StdSchedulerFactory();
Scheduler scheduler = schedulerFactory.getScheduler();
scheduler.scheduleJob(jobDetail, trigger);
scheduler.start();
任務的執行方式:串行 / 並行
防止並行
使用註解方式:註解在 org.quartz 包的內部,註解在Job類上邊
@DisallowConcurrentExecution:禁止任務併發執行
@PersistJobDataAfterExecution:正常執行完任務後,JobDataMap 中的數據應該被改動,以被下一次調用時使用
import lombok.extern.log4j.Log4j2;
import org.quartz.*;
/**
* Job 是 定時任務的具體執行邏輯
* JobDetail 是 定時任務的定義
* 註解 DisallowConcurrentExecution:禁止任務併發執行
* 註解 PersistJobDataAfterExecution:正常執行完任務後,JobDataMap 中的數據應該被改動,以被下一次調用時用
* @author EalenXie
* @date 2019/7/10
*/
@DisallowConcurrentExecution
@PersistJobDataAfterExecution
@Log4j2
public class SayHelloJobLogic implements Job {
@Override
public void execute(JobExecutionContext jobExecutionContext) throws JobExecutionException {
// 任務具體邏輯
log.info("任務(共5秒)開始 SayHelloJob.execute , start... ");
// 延時5秒
try {
Thread.sleep(5000);
} catch (InterruptedException e) {
e.printStackTrace();
}
log.info("任務(共5秒)結束 SayHelloJob.execute , end... ");
}
}
並行
較新版本的quartz,StatefulJob 這個類已經過期,所以有狀態的Job類,指的是沒有並行執行那兩個註解(@DisallowConcurrentExecution、@PersistJobDataAfterExecution)的Job類、
觸發器的 misfire 機制
Quartz框架(四)—misfire處理機制
任務框架quartz的misfire的理解
在Quartz中,當一個持久化的觸發器因爲:
- 調度器被關閉; scheduler shuwdown
- 線程池沒有可用線程; pool no free threads
- 項目重啓; project restart
- 任務的串行(併發)執行; jobs excute concurrently / serially
而錯過激活時間,就會發生激活失敗(misfire)。 miss trigger time. / misfire
CronTrigger 使用的策略
- 所有的misfile任務馬上執行
- CornTrigger默認策略,合併部分misfire,正常執行下一個週期的任務。
比如在恢復後,共有兩個misFire,則合併爲一個misFire,於是只執行這一個misFire - 所有的misFire都不管,執行下一個週期的任務。
// 所有的misfile任務馬上執行
public static final int MISFIRE_INSTRUCTION_IGNORE_MISFIRE_POLICY = -1;
// 在Trigger中默認選擇 MISFIRE_INSTRUCTION_FIRE_ONCE_NOW 策略
public static final int MISFIRE_INSTRUCTION_SMART_POLICY = 0;
// CornTrigger默認策略,合併部分misfire,正常執行下一個週期的任務。
// 比如在恢復後,共有兩個misFire,則合併爲一個misFire,於是只執行這一個misFire
public static final int MISFIRE_INSTRUCTION_FIRE_ONCE_NOW = 1;
// 所有的misFire都不管,執行下一個週期的任務。
public static final int MISFIRE_INSTRUCTION_DO_NOTHING = 2;
對應 CronTrigger 類的內部源代碼:
public CronScheduleBuilder withMisfireHandlingInstructionIgnoreMisfires() {
this.misfireInstruction = -1;
return this;
}
public CronScheduleBuilder withMisfireHandlingInstructionFireAndProceed() {
this.misfireInstruction = 1;
return this;
}
public CronScheduleBuilder withMisfireHandlingInstructionDoNothing() {
this.misfireInstruction = 2;
return this;
}
在觸發器的定義中選擇策略:TriggerBuilder.newTrigger()
TriggerBuilder.newTrigger()
// trigger 名稱、組名
.withIdentity(jobKey.getName(), jobKey.getGroup())
// trigger 描述
.withDescription(description)
// 使用 Cron 和對應的調度器 CronScheduleBuilder
.withSchedule(CronScheduleBuilder
.cronSchedule(cronExpression)
// 選擇 misFire 策略
// 1.所有的 misFire 都不管,執行下一個週期的任務。
.withMisfireHandlingInstructionDoNothing()
// 不選擇任何策略的話,則默認爲該策略
// 2.CronTrigger 默認策略,合併部分misfire,正常執行下一個週期的任務
// .withMisfireHandlingInstructionFireAndProceed()
// 3. 所有的misfire任務馬上執行
// .withMisfireHandlingInstructionIgnoreMisfires()
)
.usingJobData(jobDataMap)
.build();
Trigger 觸發器狀態
正常獲取、觸發任務執行的流程:
一個觸發器只能綁定一個Job,但是一個Job可以有多個觸發器
調度器線程執行的時候,首先從triggers表中獲取狀態爲WAITING,並且將要觸發的Trigger。然後將WAITING狀態更新爲ACQUIRED,表示該觸發器搶佔到了,防止其他調度器(實例)搶佔。然後插入觸發器信息以及實例名到FRIED_TRIGGERS表中,狀態爲ACQUIRED。前面的更新和後面的插入是在一個事務中進行的。
該觸發器搶佔到任務後,等待觸發時間的到來。
執行時間到來後,每觸發一次任務,都會在 FIRED_TRIGGERS 表中創建一條記錄,並且狀態爲 EXECUTING
如果任務允許併發執行,此時TRIGGERS表裏的狀態更新爲 WAITING,PAUSED,COMPLETE(不需要執行)。
如果任務不允許併發執行,還會把Triggers表裏的狀態更新爲BLOCK或PAUSED_BLOCK。
-
注意:Triggers表更新時根據 任務名 和 任務所屬組名 來更新,而不是 觸發器名稱 和 觸發器組名 來更新的。
這就解決了一個任務有多個觸發器的併發問題;然後觸發器線程會創建一個執行環境來執行任務,以便在任務執行完成後更新觸發器的狀態。任務執行完成後,在一個事務中觸發器狀態更新爲WAITING,刪除FIRED_TRIGGERS表裏對應的記錄。 -
如何避免多個節點執行同一個任務:
qrtz_trigger 表中有 NEXT_FIRE_TIME 字段(下一次觸發時間)。每個任務在即將執行的時候,獲取qrtz_locks 表中的行級鎖,開啓一個事務(更新 qrtz_trigger 表狀態爲ACQUIRED,並且插入 qrtz_fire_trigger 一條數據,起始狀態爲 ACQUIRED)。
異步通知
一個Job綁定多個觸發器
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.test.context.junit4.SpringRunner;
import com.example.execute.SayHelloJobLogic;
import lombok.extern.log4j.Log4j2;
import org.quartz.*;
import org.quartz.impl.StdSchedulerFactory;
import static org.quartz.TriggerBuilder.newTrigger;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.Calendar;
/**
* 測試類
*@author 小胖
*@date 2019/07
**/
@Log4j2
@RunWith(SpringRunner.class)
@SpringBootTest
public class TestQuartzForMoreJob {
/**
* 一個任務Job 綁定多個觸發器
* @author 小胖
* @date 2019/07
*/
@Test
public void MultiTriggersTest() {
// 設置執行次數:1次
SimpleScheduleBuilder simpleScheduleBuilder =
SimpleScheduleBuilder.repeatSecondlyForTotalCount(1);
// 定義任務信息:前面列出的佔用時間5秒的打印文字Job
JobDetail jobDetail = JobBuilder.newJob(SayHelloJobLogic.class)
.withIdentity("job1", "job_group1")
.storeDurably()
.build();
// 定義時間格式變量
SimpleDateFormat simpleDateFormat = new SimpleDateFormat("yyyy-MM-dd hh:mm:ss");
// 當前時間
Date date = new Date();
// 時間操作,當前時間加 5秒
Date date1 = addSecond(date, 5);
// 時間操作,當前時間加 15秒
Date date2 = addSecond(date, 15);
// 日誌打印
// 當前時間, 如 2020-02-20 09:00:00
log.info("獲取到任務的時間:" + simpleDateFormat.format(date));
// 當前時間 + 5s, 如 2020-02-20 09:00:05
log.info("第一次通知的時間:" + simpleDateFormat.format(date1));
// 當前時間 + 15s,如 2020-02-20 09:00:15
log.info("第二次通知的時間:" + simpleDateFormat.format(date2));
// 觸發器1:當前時間 + 5s
SimpleTrigger trigger1 = newTrigger().withIdentity("trigger1", "group1")
.withSchedule(simpleScheduleBuilder)
.startAt(date1)
.forJob(new JobKey("job1", "job_group1"))
.withDescription("trigger1, + 5s ")
.build();
// 觸發器2:當前時間 + 15s
SimpleTrigger trigger2 = newTrigger().withIdentity("trigger2", "group1")
.withSchedule(simpleScheduleBuilder)
.startAt(date2)
.forJob(new JobKey("job1", "job_group1"))
.withDescription("trigger2, + 15s ")
.build();
try {
Scheduler scheduler = StdSchedulerFactory.getDefaultScheduler();
// 多觸發器關聯
scheduler.scheduleJob(jobDetail, trigger1);
scheduler.scheduleJob(trigger2);
scheduler.start();
// 任務完成,再過100s,關閉調度器
Thread.sleep(100000);
scheduler.shutdown();
} catch (SchedulerException | InterruptedException e) {
e.printStackTrace();
}
}
/**
* 時間操作,對一個具體的時間增加多少秒
* @param date 時間
* @param second 增加的秒數
* @return java.util.Date
* @author 小胖
* @date 2019/07
*/
public static Date addSecond(Date date, int second) {
java.util.Calendar calendar = java.util.Calendar.getInstance();
calendar.setTime(date);
// calendar.add(Calendar.MINUTE, minute);
calendar.add(Calendar.SECOND, second);
return calendar.getTime();
}
}
測試截圖:
創建任務的步驟優化
/**
* Quartz 裏面 一個最簡單,最基本的定時任務 應該包含以下必要基本屬性
* 注意 : 這裏只是方便演示,故寫此類進行簡單說明, 可針對自身業務對此類進行擴展
* @author EalenXie
* @date 2019/07
*/
@Data
@Builder
public class JobInfo {
/**
* 定時任務 的名字和分組名
*/
@NotNull(message = "定時任務的 名字 和 組名 堅決不爲空")
private JobKey jobKey;
/**
* 定時任務 的描述(可以定時任務本身的描述,也可以是觸發器的)
*/
private String description;
/**
* 定時任務 的執行cron
*/
@NotEmpty(message = "定時任務的執行cron 不能爲空")
private String cronExpression;
/**
* 定時任務 的元數據
*/
private Map<?, ?> jobDataMap;
/**
* 定時任務 的 具體執行邏輯類
*/
@NotNull(message = "定時任務的具體執行邏輯類 堅決不能爲空")
private Class<? extends Job> jobClass;
}
/**
* 創建和啓動 定時任務
* @param jobInfo 定時任務
*/
public void startJob(JobInfo jobInfo) throws SchedulerException {
// 1.定時任務 的 名字和組名
JobKey jobKey = jobInfo.getJobKey();
// 2.定時任務 的 元數據
JobDataMap jobDataMap = getJobDataMap(jobInfo.getJobDataMap());
// 3.定時任務 的 描述
String description = jobInfo.getDescription();
// 4.定時任務 的 邏輯實現類
Class<? extends Job> jobClass = jobInfo.getJobClass();
// 創建 JobDetail 、Trigger
JobDetail jobDetail = getJobDetail(jobKey, description, jobDataMap, jobClass);
Trigger trigger = getTrigger(jobKey, description, jobDataMap, cron);
// 啓動調度器
scheduler.scheduleJob(jobDetail, trigger);
}
/**
* 獲取定時任務的定義
* JobDetail是任務的定義, Job是任務的執行邏輯
* @param jobKey 定時任務的名稱 組名
* @param description 定時任務的 描述
* @param jobDataMap 定時任務的 元數據
* @param jobClass 定時任務的 真正執行邏輯定義類
* @return org.quartz.JobDetail
*/
public JobDetail getJobDetail(JobKey jobKey,
String description,
JobDataMap jobDataMap,
Class<? extends Job> jobClass) {
return JobBuilder.newJob(jobClass)
.withIdentity(jobKey)
.withDescription(description)
.setJobData(jobDataMap)
.usingJobData(jobDataMap)
.requestRecovery()
.storeDurably()
.build();
}
/**
* 獲取Trigger (Job的觸發器,執行規則)
* @param jobKey 定時任務的名稱 組名
* @param description 定時任務的 描述
* @param jobDataMap 定時任務的 元數據
* @param cronExpression 定時任務的 執行cron表達式
* @return org.quartz.Trigger
*/
public Trigger getTrigger(JobKey jobKey,
String description,
JobDataMap jobDataMap,
String cronExpression) {
return TriggerBuilder.newTrigger()
// trigger 名稱、組名
.withIdentity(jobKey.getName(), jobKey.getGroup())
// trigger 描述
.withDescription(description)
//
.withSchedule(CronScheduleBuilder
.cronSchedule(cronExpression)
// 選擇 misFire 策略:
// 1. 所有的 misFire 都不管,執行下一個週期的任務。
.withMisfireHandlingInstructionDoNothing()
// 2.CronTrigger 默認策略,合併部分misfire,正常執行下一個週期的任務
// .withMisfireHandlingInstructionFireAndProceed()
// 3. 所有的misfile任務馬上執行
// .withMisfireHandlingInstructionIgnoreMisfires()
)
.usingJobData(jobDataMap)
.build();
}
/**
* 獲取任務的元數據
* @param map 不限量的(序列化的)數據對象
* @return org.quartz.JobDataMap
*/
public JobDataMap getJobDataMap(Map<?, ?> map) {
return map == null ? new JobDataMap() : new JobDataMap(map);
}
監聽器 Listener
用於當任務調度中你所關注事件發生時,能夠及時獲取這一事件的通知。類似於任務執行過程中的郵件、短信類的提醒;
對Job(任務)建立一個監聽器,分別對任務執行 《之前, 之後, 取消》 3個階段進行監聽;
實現監聽器需要實現JobListener接口,然後註冊到Scheduler上;
- 全局監聽器與非全局監聽器,二者的區別在於:
全局監聽器:能夠接收到所有的Job/Trigger的事件通知,
非全局監聽器:只能接收到在其上註冊的Job或Trigger的事件,不在其上註冊的Job或Trigger則不會進行監聽
Quartz框架(十)監聽
Quartz使用(4) - Quartz監聽器Listerner
【Quartz】 JobListener、Triggerlistener、SchedulerListener介紹與使用
JobListener
任務調度過程中,與任務Job相關的事件包括:Job開始要執行的提示;;Job執行完成的提示燈
Quartz任務調度(4) JobListener分版本超詳細解析
// (1) Job Listener
// 監聽器 Job Listener
MyJobListener myJobListener = new MyJobListener();
// 指定任務的 Job Listener
KeyMatcher<JobKey> jobMatcher = KeyMatcher.keyEquals(jobDetail.getKey());
// 指定任務的 Job Listener
KeyMatcher<JobKey> cronJobMatcher = KeyMatcher.keyEquals(nonStateJobDetail.getKey());
// 指定組的 Job Listener
GroupMatcher<JobKey> groupJobMatcher = GroupMatcher.jobGroupEquals("group2");
// 創建並註冊,局部的 Job Listener
// scheduler.getListenerManager().addJobListener(myJobListener, jobMatcher);
// scheduler.getListenerManager().addJobListener(myJobListener, cronJobMatcher);
// 創建並註冊,全局的 Job Listener
scheduler.getListenerManager().addJobListener(myJobListener, EverythingMatcher.allJobs());
TriggerListener
任務調度過程中,與觸發器Trigger相關的事件包括:觸發器觸發、觸發器未正常觸發、觸發器完成等。
Quartz任務調度(5) TriggerListener分版本超詳細解析
// (2) Trigger Listener
MyTriggerListener myTriggerListener = new MyTriggerListener("MyTriggerListener");
// 獲取 triggerKey
TriggerKey triggerKey = trigger.getKey();
TriggerKey cronTriggerKey = cronTrigger.getKey();
// 指定任務的 Trigger Listener
KeyMatcher<TriggerKey> triggerMatcher = KeyMatcher.keyEquals(triggerKey);
KeyMatcher<TriggerKey> cronTriggerMatcher = KeyMatcher.keyEquals(cronTriggerKey);
// 一個特定組的 Trigger Listener
// GroupMatcher<TriggerKey> groupTriggerMatcher = GroupMatcher.groupEquals("group1");
// GroupMatcher<TriggerKey> groupTriggerMatcher1 = GroupMatcher.groupStartsWith("g");
// GroupMatcher<TriggerKey> groupTriggerMatcher2 = GroupMatcher.groupEndsWith("2");
// 創建並註冊,局部的 Trigger Listener
// scheduler.getListenerManager().addTriggerListener(myTriggerListener,triggerMatcher);
// scheduler.getListenerManager().addTriggerListener(myTriggerListener, cronTriggerMatcher);
// 全局的 Trigger Listener
scheduler.getListenerManager().addTriggerListener(myTriggerListener, EverythingMatcher.allTriggers());
SchedulerListener
SchedulerListener會在Scheduler的生命週期中關鍵事件發生時被調用。
與Scheduler有關的事件包括:增加一個Job/Trigger,刪除一個Job/Trigger,Scheduler發生嚴重錯誤,關閉Scheduler等。
Quartz任務調度(6) SchedulerListener分版本超詳細解析
// (3) Scheduler Listener
MySchedulerListener mySchedulerListener = new MySchedulerListener();
scheduler.getListenerManager().addSchedulerListener(mySchedulerListener);
// 5. 啓動任務
// 啓動調度器
log.info("開啓調度器 scheduler start...");
scheduler.start();
// 20秒
try {
Thread.sleep(20000);
} catch (InterruptedException e) {
e.printStackTrace();
}
// 暫停Job
scheduler.pauseJob(jobDetail.getKey());
scheduler.pauseJob(nonStateJobDetail.getKey());
// 綁定新的 CronTrigger
String newCron = "0/15 * * * * ? ";
CronTrigger cronTrigger1 = TriggerBuilder.newTrigger()
.withIdentity("newCronTrigger", "group2")
.withDescription("a new cron")
.startNow()
.withSchedule(CronScheduleBuilder.cronSchedule(newCron))
.forJob("nonStatedJob", "group2")
.build();
TriggerKey triggerKey1 = cronTrigger1.getKey();
scheduler.rescheduleJob(triggerKey1, cronTrigger1);
// 恢復運行Job
scheduler.resumeJob(jobDetail.getKey());
// 20秒
try {
Thread.sleep(20000);
} catch (InterruptedException e) {
e.printStackTrace();
}
// 刪除Job
scheduler.deleteJob(jobDetail.getKey());
// 卸載觸發器
scheduler.unscheduleJob(triggerKey);
scheduler.unscheduleJob(cronTriggerKey);
// 6. 停止任務
// 停止調度器
log.info("停止調度器 scheduler shutdown...");
scheduler.shutdown();
Quartz 常見問題
Trigger does not reference given job!
java quartz框架創建定時任務異常: Trigger does not reference given job!
Quartz調度器-觸發器不引用給定的作業:Trigger does not reference given job
- 檢查 JobDetail 定義的 .withIdentity() 的內容,是否與 Trigger 定義的 .forJob() 的內容是否相同,不同則會報錯。
JobDetail jobDetail =
JobBuilder.newJob(MyJob1.class)
.withIdentity("job1", "group1")
.withDescription("jobDetail des.")
.build();
Trigger trigger =
TriggerBuilder.newTrigger()
.withIdentity("SimpleTrigger1", "SimpleGroup")
.withDescription("SimpleTrigger1 des.")
.startNow()
.withSchedule(SimpleScheduleBuilder
.simpleSchedule()
// 間隔
.withIntervalInSeconds(2)
// 重複次數
.repeatForever()
// 若失效,之後再恢復並馬上執行
.withMisfireHandlingInstructionFireNow()
)
// 檢查此處是否與將要綁定的JobDetail中的Job名稱和組名稱是否相同
.forJob("job1", "group1")
.build();
SchedulerFactory schedulerFactory = new StdSchedulerFactory();
Scheduler scheduler = schedulerFactory.getScheduler();
// 監聽器
MyJobListener myJobListener = new MyJobListener();
KeyMatcher<JobKey> keyMatcher = KeyMatcher.keyEquals(jobDetail.getKey());
scheduler.getListenerManager().addJobListener(myJobListener, keyMatcher);
// 若報錯(Trigger does not reference given job)一般會指向這裏
scheduler.scheduleJob(jobDetail, trigger);