Quartz學習(六)--錯過的任務怎麼辦?

不知道大家在用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

發佈了27 篇原創文章 · 獲贊 22 · 訪問量 10萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章