感謝兄台: 《quartz-misfire 錯失、補償執行》
misfire定義
misfire:被錯過的執行任務策略
misfire重現——CronTrigger
job任務類:
package org.quartz.examples.example5; import org.quartz.DisallowConcurrentExecution; import org.quartz.Job; import org.quartz.JobDataMap; import org.quartz.JobExecutionContext; import org.quartz.JobExecutionException; import org.quartz.PersistJobDataAfterExecution; import java.text.SimpleDateFormat; import java.util.Date; /** * <pre> * 任務job。 * 因為@DisallowConcurrentExecution注解,所以這個job不可以被多個定時器或觸發器同時執行,否則會觸發定時器的misfire,就需要我們定義好定時器的misfire策略。 * 如果不定義misfire,會出現 * </pre> */ @PersistJobDataAfterExecution //持久化JobDataMap里的數據,使下一個定時任務還能獲取到這些值 @DisallowConcurrentExecution //禁止並發多任務執行,所以永遠只有一個任務在執行中 public class StatefulDumbJob implements Job { //任務執行計數器 public static final String NUM_EXECUTIONS = "NumExecutions"; public static final String EXECUTION_DELAY = "ExecutionDelay"; public static final SimpleDateFormat SDF = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss.SSS"); //必須要有public修飾的無參構造函數 public StatefulDumbJob() { } //定時器執行方法 public void execute(JobExecutionContext context) throws JobExecutionException { System.out.println("---" + context.getJobDetail().getKey() + " executing. [" + SDF.format(new Date()) + "]"); //獲得帶狀態集合 JobDataMap map = context.getJobDetail().getJobDataMap(); int executeCount = 0; if (map.containsKey(NUM_EXECUTIONS)) { executeCount = map.getInt(NUM_EXECUTIONS); } executeCount++; map.put(NUM_EXECUTIONS, executeCount); System.out.println(" -" + context.getJobDetail().getKey() + " complete (" + executeCount + ").[" + SDF.format(new Date())+ "]"); } }
定時器類:
package org.quartz.examples.example5; import static org.quartz.CronScheduleBuilder.cronSchedule; import static org.quartz.DateBuilder.nextGivenSecondDate; import static org.quartz.JobBuilder.newJob; import static org.quartz.TriggerBuilder.newTrigger; import java.util.Date; import org.quartz.CronExpression; import org.quartz.CronScheduleBuilder; import org.quartz.CronTrigger; import org.quartz.JobDetail; import org.quartz.SchedulerFactory; import org.quartz.impl.StdScheduler; import org.quartz.impl.StdSchedulerFactory; import org.slf4j.Logger; import org.slf4j.LoggerFactory; /** * <pre> 此類演示了如何定義定時器的misfire策略。misfire:被錯過的執行任務策略 * </pre> */ public class MisfireExample_CronScheduleBuilder { static final Logger LOG = LoggerFactory.getLogger(MisfireExample_CronScheduleBuilder.class); public static void main(String[] args) throws Exception { // 初始化一個調度工廠,並實例化一個調度類 SchedulerFactory sf = new StdSchedulerFactory(); // Scheduler sched = sf.getScheduler(); StdScheduler sched = (StdScheduler) sf.getScheduler(); // 第一個參數:null就是默認當前時間,也可以指定時間 // 第二個參數:把一分鍾按10進行划分,也就是60/10等份。 // 舉例:當前時間是10:26:04,那么startTime就是10:30:00。當前時間是10:38:31,那么startTime就是10:40:00。 Date startTime = nextGivenSecondDate(null, 10); JobDetail job = newJob(StatefulDumbJob.class).withIdentity("job1", "group1") // .usingJobData(StatefulDumbJob.EXECUTION_DELAY, 30000L) .build(); String cron = "0/2 * * * * ?"; // 每2秒執行一次 CronScheduleBuilder cronScheduleBuilder = cronSchedule(new CronExpression(cron)); // ======================================================================== // ======================== misfire定義,開始 ======================= // ======================================================================== /* 設置misfire策略:CronTrigger.MISFIRE_INSTRUCTION_DO_NOTHING = 2 ——不觸發立即執行 ——等待下次Cron觸發頻率到達時刻開始按照Cron頻率依次執行 */ cronScheduleBuilder.withMisfireHandlingInstructionDoNothing(); /* 設置misfire策略:Trigger.MISFIRE_INSTRUCTION_IGNORE_MISFIRE_POLICY = -1 ——以錯過的第一個頻率時間立刻開始執行 ——重做錯過的所有頻率周期 ——當下一次觸發頻率發生時間大於當前時間以后,按照Interval的依次執行剩下的頻率 ——共執行RepeatCount+1次 */ // cronScheduleBuilder.withMisfireHandlingInstructionIgnoreMisfires(); /* * 設置misfire策略:CronTrigger.MISFIRE_INSTRUCTION_FIRE_ONCE_NOW = 1 * 以當前時間為觸發頻率立刻觸發一次執行,然后按照Cron頻率依次執行 */ // cronScheduleBuilder.withMisfireHandlingInstructionFireAndProceed(); // ======================================================================== // ======================== misfire定義,結束 ======================= // ======================================================================== CronTrigger trigger = newTrigger().withIdentity("trigger1", "group1").startAt(startTime) .withSchedule(cronScheduleBuilder).build(); // CronTrigger默認misfire策略是: org.quartz.Trigger.MISFIRE_INSTRUCTION_SMART_POLICY = 0 int misfireInstruction = trigger.getMisfireInstruction(); LOG.info("當前misfire策略:" + misfireInstruction); Date ft = sched.scheduleJob(job, trigger); LOG.info(job.getKey().toString()); sched.start(); LOG.info("調度器啟動,主線程睡眠15秒!!!!調度器內任務線程繼續執行。"); Thread.sleep(15L * 1000L); // // 暫停觸發器 // sched.pauseTrigger(trigger.getKey()); // // 繼續觸發器 // sched.resumeTrigger(trigger.getKey()); // 暫停執行任務 sched.pauseJob(job.getKey()); LOG.info("調度器暫停執行定時器,主線程睡眠11秒!!!!會錯過執行job1的N次定時任務。模擬當定時器的執行線程由於搶不到CPU時間或其他事件錯過執行的情況。"); Thread.sleep(11L * 1000L); // 繼續執行任務 sched.resumeJob(job.getKey()); //當定時器得到繼續執行的命令時,被錯過執行的任務次數,就會按照misfire的定義去執行 LOG.info("調度器繼續執行定時器,主線程睡眠15秒!!!!調度器內任務線程繼續執行。"); Thread.sleep(15L * 1000L); LOG.info("調度器終止執行!!!!"); sched.shutdown(true); } }
misfire重現——SimpleTrigger
CronTrigger的misfire策略經本人親自測試,注釋全部可靠,SimpleTrigger全部來自以下兄台文章注釋,實際結果請各位親自實驗。
感謝兄台: 《quartz-misfire 錯失、補償執行》
SimpleTrigger默認misfire策略也是: org.quartz.Trigger.MISFIRE_INSTRUCTION_SMART_POLICY = 0
//定義觸發器,2秒執行一次,循環5次,總共執行6次 SimpleScheduleBuilder simpleScheduleBuilder = simpleSchedule().withIntervalInSeconds(2).withRepeatCount(5); // .repeatForever(); //無限循環 /* 設置misfire策略為:SimpleTrigger.MISFIRE_INSTRUCTION_RESCHEDULE_NEXT_WITH_EXISTING_COUNT = 5 ——不觸發立即執行 ——等待下次觸發頻率周期時刻,執行至FinalTime的剩余周期次數 ——以startTime為基准計算周期頻率,並得到FinalTime ——即使中間出現pause,resume以后保持FinalTime時間不變 */ simpleScheduleBuilder.withMisfireHandlingInstructionNextWithExistingCount(); /* 設置misfire策略為:SimpleTrigger.MISFIRE_INSTRUCTION_RESCHEDULE_NEXT_WITH_REMAINING_COUNT = 4 ——不觸發立即執行 ——等待下次觸發頻率周期時刻,執行至FinalTime的剩余周期次數 ——以startTime為基准計算周期頻率,並得到FinalTime ——即使中間出現pause,resume以后保持FinalTime時間不變 */ simpleScheduleBuilder.withMisfireHandlingInstructionNextWithRemainingCount(); /* 設置misfire策略為:SimpleTrigger.MISFIRE_INSTRUCTION_RESCHEDULE_NOW_WITH_REMAINING_REPEAT_COUNT = 3 ——以當前時間為觸發頻率立即觸發執行 ——執行至FinalTIme的剩余周期次數 ——以調度或恢復調度的時刻為基准的周期頻率,FinalTime根據剩余次數和當前時間計算得到 ——調整后的FinalTime會略大於根據starttime計算的到的FinalTime值 */ simpleScheduleBuilder.withMisfireHandlingInstructionNowWithRemainingCount(); /* 設置misfire策略為:SimpleTrigger.MISFIRE_INSTRUCTION_RESCHEDULE_NOW_WITH_EXISTING_REPEAT_COUNT = 2 ——以當前時間為觸發頻率立即觸發執行 ——執行至FinalTIme的剩余周期次數 ——以調度或恢復調度的時刻為基准的周期頻率,FinalTime根據剩余次數和當前時間計算得到 ——調整后的FinalTime會略大於根據starttime計算的到的FinalTime值 */ simpleScheduleBuilder.withMisfireHandlingInstructionNowWithExistingCount(); /* 設置misfire策略為:SimpleTrigger.MISFIRE_INSTRUCTION_FIRE_NOW = 1 ——以當前時間為觸發頻率立即觸發執行 ——執行至FinalTIme的剩余周期次數 ——以調度或恢復調度的時刻為基准的周期頻率,FinalTime根據剩余次數和當前時間計算得到 ——調整后的FinalTime會略大於根據starttime計算的到的FinalTime值 */ simpleScheduleBuilder.withMisfireHandlingInstructionFireNow(); /* 設置misfire策略為:Trigger.MISFIRE_INSTRUCTION_IGNORE_MISFIRE_POLICY = -1 ——以錯過的第一個頻率時間立刻開始執行 ——重做錯過的所有頻率周期 ——當下一次觸發頻率發生時間大於當前時間以后,按照Interval的依次執行剩下的頻率 ——共執行RepeatCount+1次 */ simpleScheduleBuilder.withMisfireHandlingInstructionIgnoreMisfires();