不知道大家在用Quartz的時候 有沒有遇到這樣一種情況:
觸發器設定每3秒鐘觸發一次 ,但是工作需要10秒鐘的執行時間.因此,在一次任務結束執行前,觸發器已經錯失觸發
當這種情況下我們怎麼處理呢,讓我們一起學習一下......
還是先貼代碼:
job類:StatefulDumbJob.java
import java.text.SimpleDateFormat;
import java.util.Calendar;
import org.quartz.DisallowConcurrentExecution;
import org.quartz.Job;
import org.quartz.JobDataMap;
import org.quartz.JobExecutionContext;
import org.quartz.JobExecutionException;
import org.quartz.PersistJobDataAfterExecution;
@PersistJobDataAfterExecution
@DisallowConcurrentExecution
public class StatefulDumbJob implements Job {
// 靜態常量,作爲任務在調用間,保持數據的鍵(key)
// NUM_EXECUTIONS,保存的計數每次遞增1
// EXECUTION_DELAY,任務在執行時,中間睡眠的時間。本例中睡眠時間過長導致了錯失觸發
public static final String NUM_EXECUTIONS = "NumExecutions";
public static final String EXECUTION_DELAY = "ExecutionDelay";
@Override
public void execute(JobExecutionContext context)
throws JobExecutionException {
// 任務執行的時間
SimpleDateFormat dateFormat = new SimpleDateFormat(
"yyyy-MM-dd HH:mm:ss");
String jobRunTime = dateFormat.format(Calendar.getInstance().getTime());
System.err.println("---" + context.getJobDetail().getKey().getName() + " 在 : ["
+ jobRunTime + "] 執行了!!");
// 任務執行計數 累加
JobDataMap map = context.getJobDetail().getJobDataMap();
int executeCount = 0;
if (map.containsKey(NUM_EXECUTIONS)) {
executeCount = map.getInt(NUM_EXECUTIONS);
}
executeCount++;
map.put(NUM_EXECUTIONS, executeCount);
// 睡眠時間: 由調度類重新設置值 ,本例爲 睡眠10s
long delay = 5000l;
if (map.containsKey(EXECUTION_DELAY)) {
delay = map.getLong(EXECUTION_DELAY);
}
try {
Thread.sleep(delay);
} catch (Exception ignore) {
}
// 睡眠醒來後,打印任務執行結束的信息
System.err.println(" -" + context.getJobDetail().getKey().getName()
+ " 完成次數 : " + executeCount );
}
}
調度類: MisfireExample.java
import static org.quartz.DateBuilder.nextGivenSecondDate;
import static org.quartz.JobBuilder.newJob;
import static org.quartz.SimpleScheduleBuilder.simpleSchedule;
import static org.quartz.TriggerBuilder.newTrigger;
import java.text.SimpleDateFormat;
import java.util.Date;
import org.quartz.JobDetail;
import org.quartz.Scheduler;
import org.quartz.SchedulerFactory;
import org.quartz.SchedulerMetaData;
import org.quartz.SimpleTrigger;
import org.quartz.impl.StdSchedulerFactory;
public class MisfireExample {
public static void main(String[] args) throws Exception {
MisfireExample example = new MisfireExample();
example.run();
}
public void run() throws Exception {
// 任務執行的時間 格式化
SimpleDateFormat dateFormat = new SimpleDateFormat(
"yyyy-MM-dd HH:mm:ss");
SchedulerFactory sf = new StdSchedulerFactory();
Scheduler sched = sf.getScheduler();
System.out.println("--------------- 初始化 -------------------");
// 下一個第15秒
Date startTime = nextGivenSecondDate(null, 15);
// statefulJob1 每3s運行一次,但它會延遲10s
JobDetail job = newJob(StatefulDumbJob.class)
.withIdentity("statefulJob1", "group1")
.usingJobData(StatefulDumbJob.EXECUTION_DELAY, 10000L) // 設置參數:睡眠時間 10s
.build();
SimpleTrigger trigger = newTrigger()
.withIdentity("trigger1", "group1")
.startAt(startTime)
.withSchedule(
simpleSchedule().withIntervalInSeconds(3)
.repeatForever()).build();
Date ft = sched.scheduleJob(job, trigger);
System.out.println(job.getKey().getName() + " 將在: " + dateFormat.format(ft)
+ " 時運行.並且重複: " + trigger.getRepeatCount() + " 次, 每次間隔 "
+ trigger.getRepeatInterval() / 1000 + " 秒");
// statefulJob2 將每3s運行一次 , 但它將延遲10s , 然後不斷的迭代
job = newJob(StatefulDumbJob.class)
.withIdentity("statefulJob2", "group1")
.usingJobData(StatefulDumbJob.EXECUTION_DELAY, 10000L)// 設置參數:睡眠時間 10s
.build();
trigger = newTrigger()
.withIdentity("trigger2", "group1")
.startAt(startTime)
.withSchedule(
simpleSchedule()
.withIntervalInSeconds(3)
.repeatForever()
// 設置錯失觸發後的調度策略
.withMisfireHandlingInstructionNowWithRemainingCount()
)
.build();
ft = sched.scheduleJob(job, trigger);
System.out.println(job.getKey().getName() + " 將在: " + dateFormat.format(ft)
+ " 時運行.並且重複: " + trigger.getRepeatCount() + " 次, 每次間隔 "
+ trigger.getRepeatInterval() / 1000 + " 秒");
System.out.println("------- 開始調度 (調用.start()方法) ----------------");
sched.start();
// 給任務留時間運行
Thread.sleep(600L * 1000L);
sched.shutdown(true);
System.out.println("------- 調度已關閉 ---------------------");
// 顯示一下 已經執行的任務信息
SchedulerMetaData metaData = sched.getMetaData();
System.out.println("~~~~~~~~~~ 執行了 "
+ metaData.getNumberOfJobsExecuted() + " 個 jobs.");
}
}
先說明 一個詞 : misfire -- 指的是 錯過了觸發時間
你會注意到2個觸發器具有相同的時間安排,相同的任務
觸發器設定每3秒鐘觸發一次,但是工作需要10秒鐘的執行時間
因此,在一次任務結束執行前,觸發器已經錯失觸發(除非’錯失觸發時限’被設置爲超過7秒)。
其中第二個任務設置自己的錯失觸發指示:.withMisfireHandlingInstructionNowWithRemainingCount()
所以當檢測到丟失觸發時,不會立即觸發,而是忽略本次安排到下一個預定時間去觸發
我們的結論 :
a) 本範例中,觸發的間隔被設置爲3秒,但是由於任務體執行間睡眠10秒,導致了錯失觸發的產生。
b) 實際運行的效果,2個任務執行的間隔爲10秒。
c) 由於丟失觸發時,job2的策略是立即觸發,而job1是等待下一次機會觸發。所以job2會趕在job1的前頭,最終運行次數大於job1。
Quartz 的 Misfire處理規則:
調度(scheduleJob)或恢復調度(resumeTrigger,resumeJob)後不同的misfire對應的處理規則
CronTrigger
withMisfireHandlingInstructionDoNothing
——不觸發立即執行
——等待下次Cron觸發頻率到達時刻開始按照Cron頻率依次執行
withMisfireHandlingInstructionIgnoreMisfires
——以錯過的第一個頻率時間立刻開始執行
——重做錯過的所有頻率週期後
——當下一次觸發頻率發生時間大於當前時間後,再按照正常的Cron頻率依次執行
withMisfireHandlingInstructionFireAndProceed
——以當前時間爲觸發頻率立刻觸發一次執行
——然後按照Cron頻率依次執行
SimpleTrigger
withMisfireHandlingInstructionFireNow
——以當前時間爲觸發頻率立即觸發執行
——執行至FinalTIme的剩餘週期次數
——以調度或恢復調度的時刻爲基準的週期頻率,FinalTime根據剩餘次數和當前時間計算得到
——調整後的FinalTime會略大於根據starttime計算的到的FinalTime值
withMisfireHandlingInstructionIgnoreMisfires
——以錯過的第一個頻率時間立刻開始執行
——重做錯過的所有頻率週期
——當下一次觸發頻率發生時間大於當前時間以後,按照Interval的依次執行剩下的頻率
——共執行RepeatCount+1次
withMisfireHandlingInstructionNextWithExistingCount
——不觸發立即執行
——等待下次觸發頻率週期時刻,執行至FinalTime的剩餘週期次數
——以startTime爲基準計算週期頻率,並得到FinalTime
——即使中間出現pause,resume以後保持FinalTime時間不變
withMisfireHandlingInstructionNowWithExistingCount
——以當前時間爲觸發頻率立即觸發執行
——執行至FinalTIme的剩餘週期次數
——以調度或恢復調度的時刻爲基準的週期頻率,FinalTime根據剩餘次數和當前時間計算得到
——調整後的FinalTime會略大於根據starttime計算的到的FinalTime值
withMisfireHandlingInstructionNextWithRemainingCount
——不觸發立即執行
——等待下次觸發頻率週期時刻,執行至FinalTime的剩餘週期次數
——以startTime爲基準計算週期頻率,並得到FinalTime
——即使中間出現pause,resume以後保持FinalTime時間不變
withMisfireHandlingInstructionNowWithRemainingCount
——以當前時間爲觸發頻率立即觸發執行
——執行至FinalTIme的剩餘週期次數
——以調度或恢復調度的時刻爲基準的週期頻率,FinalTime根據剩餘次數和當前時間計算得到
——調整後的FinalTime會略大於根據starttime計算的到的FinalTime值
MISFIRE_INSTRUCTION_RESCHEDULE_NOW_WITH_REMAINING_REPEAT_COUNT
——此指令導致trigger忘記原始設置的starttime和repeat-count
——觸發器的repeat-count將被設置爲剩餘的次數
——這樣會導致後面無法獲得原始設定的starttime和repeat-count值
另外,如果任務數超過了Quartz的線程池中的線程數時,也會發生類似的情況 兄弟們可以參考一下下面的這篇博文:
http://www.cnblogs.com/makemelaugh/archive/2012/06/17/2533105.html