Quartz是一個特性豐富的開源的任務調度開發庫,它可以很方便的集成到你的應用程序中。在Quartz中,當一個持久的觸發器因為調度器被關閉或者線程池中沒有可用的線程而錯過了激活時間時,就會發生激活失敗(misfire)。那么,我們需要明確2個問題:如何判定激活失敗;如何處理激活失敗。
一、激活失敗判定
quartz.properties配置文件中有一個屬性是misfireThreshold(單位為毫秒),用來指定調度引擎設置觸發器超時的"臨界值"。也就是說Quartz對於任務的超時是有容忍度的,超過了這個容忍度才會判定misfire。比如說,某觸發器設置為,10:15首次激活,然后每隔3秒激活一次,無限次重復。然而該任務每次運行需要10秒鍾的時間。可見,每次任務的執行都會超時,那么究竟是否會引起misfire,就取決於misfireThreshold的值了。以第二次任務來說,它的運行時間已經比預定晚了7秒,那么如果misfireThreshold>7000,說明該偏差可容忍,則不算misfire,該任務立刻執行;如果misfireThreshold<=7000,則判定為misfire,根據相關配置策略進行處理。
注意,任務的延遲是有累計的。在前面的例子中,假設misfireThreshold設置為60000,即60秒。那么每次任務的延遲量即是否misfire計算如下:
任務編號 |
預定運行時刻 |
實際運行時刻 |
延遲量(秒) |
備注 |
1 |
10:15 |
10:15 |
0 |
|
2 |
10:18 |
10:25 |
7 |
|
3 |
10:21 |
10:35 |
14 |
|
4 |
10:24 |
10:45 |
21 |
|
5 |
10:27 |
10:55 |
28 |
|
6 |
10:30 |
11:05 |
35 |
|
7 |
10:33 |
11:15 |
42 |
|
8 |
10:36 |
11:25 |
49 |
|
9 |
10:39 |
11:35 |
56 |
|
10 |
10:42 |
11:45 |
63 |
misfire |
從表中可以看到,每一次任務執行都會帶來7秒的延遲量,該延遲量不斷被與misfireThreshold比較,直到達到該值后,在11:45分發生misfire。那么在11:45第10次任務會不會准時執行呢?答案是不一定,取決於配置。
二、激活失敗處理
激活失敗指令(Misfire Instructions)是觸發器的一個重要屬性,它指定了misfire發生時調度器應當如何處理。所有類型的觸發器都有一個默認的指令,叫做Trigger.MISFIRE_INSTRUCTION_SMART_POLICY,但是這個這個“聰明策略”對於不同類型的觸發器其具體行為是不同的。對於SimpleTrigger,這個“聰明策略”將根據觸發器實例的狀態和配置來決定其行為。具體如下[]:
如果Repeat Count=0:
instruction selected = MISFIRE_INSTRUCTION_FIRE_NOW;
如果Repeat Count=REPEAT_INDEFINITELY:
instruction selected = MISFIRE_INSTRUCTION_RESCHEDULE_NEXT_WITH_REMAINING_COUNT;
如果Repeat Count>0:
instruction selected = MISFIRE_INSTRUCTION_RESCHEDULE_NOW_WITH_EXISTING_REPEAT_COUNT;
下面解釋SimpleTrigger常見策略:
MISFIRE_INSTRUCTION_FIRE_NOW
立刻執行。對於不會重復執行的任務,這是默認的處理策略。
MISFIRE_INSTRUCTION_RESCHEDULE_NEXT_WITH_REMAINING_COUNT
NEXT指以現在為基准,以repeat interval為周期,延時到下一個激活點執行。WITH_REMAINING_COUNT指超時期內錯過的執行機會作廢。因此該策略的含義是,在下一個激活點執行,且超時期內錯過的執行機會作廢。
MISFIRE_INSTRUCTION_RESCHEDULE_NOW_WITH_REMAINING_COUNT
立即執行,且超時期內錯過的執行機會作廢。
MISFIRE_INSTRUCTION_RESCHEDULE_NEXT_WITH_EXISTING_COUNT
WITH_EXISTING_COUNT指,根據已設置的repeat count進行執行。也就是說錯過的執行機會不作廢,保證實際執行次數滿足設置。因此本策略的含義是,在下一個激活點執行,並重復到指定的次數。
MISFIRE_INSTRUCTION_RESCHEDULE_NOW_WITH_EXISTING_COUNT
立即執行,並重復到指定的次數。
MISFIRE_INSTRUCTION_IGNORE_MISFIRE_POLICY
忽略所有的超時狀態,按照觸發器的策略執行。
對於CronTrigger,該“聰明策略”默認選擇MISFIRE_INSTRUCTION_FIRE_ONCE_NOW以指導其行為。
下面解釋CronTrigger常見策略:
MISFIRE_INSTRUCTION_FIRE_ONCE_NOW
立刻執行一次,然后就按照正常的計划執行。
MISFIRE_INSTRUCTION_DO_NOTHING
目前不執行,然后就按照正常的計划執行。這意味着如果下次執行時間超過了end time,實際上就沒有執行機會了。
三、示例程序1
=====代碼=====
JobDetail job1 = newJob(StatefulDumbJob.class).withIdentity("statefulJob1", "group1") .usingJobData(StatefulDumbJob.EXECUTION_DELAY, 10000L).build(); SimpleTrigger trigger1 = newTrigger().withIdentity("trigger1", "group1").startAt(startTime) .withSchedule(simpleSchedule().withIntervalInSeconds(3).repeatForever()).build(); Date ft1 = sched.scheduleJob(job1, trigger1); log.info(job1.getKey() + " will run at: " + ft1 + " and repeat: " + trigger1.getRepeatCount() + " times, every " + trigger1.getRepeatInterval() / 1000 + " seconds");
=====代碼=====
misfireThreshold設置為60000。
分析:
各任務的延遲量如下表所示:
任務編號 |
預定運行時刻 |
實際運行時刻 |
延遲量(秒) |
備注 |
1 |
16:15:45 |
16:15:45 |
0 |
|
2 |
16:15:48 |
16:15:55 |
7 |
|
3 |
16:15:51 |
16:16:05 |
14 |
|
4 |
16:15:54 |
16:16:15 |
21 |
|
5 |
16:15:57 |
16:16:25 |
28 |
|
6 |
16:16:00 |
16:16:35 |
35 |
|
7 |
16:16:03 |
16:16:45 |
42 |
|
8 |
16:16:06 |
16:16:55 |
49 |
|
9 |
16:16:09 |
16:17:05 |
56 |
|
無 |
16:16:12 |
16:17:15 |
63 |
misfire |
10 |
16:17:18 |
16:17:18 |
0 |
|
11 |
16:17:21 |
16:17:28 |
7 |
|
在16:17:15發生misfire。因為Repeat Count=REPEAT_INDEFINITELY,所以smart policy選擇的策略是MISFIRE_INSTRUCTION_RESCHEDULE_NEXT_WITH_REMAINING_COUNT。因此,延時3秒后,在16:17:18第10次任務被執行,延遲量清零。當然,程序會一直這樣循環下去。
程序輸出:
---group1.statefulJob1 executing.[Fri Feb 19 16:15:45 CST 2016]
-group1.statefulJob1 complete (1).
---group1.statefulJob1 executing.[Fri Feb 19 16:15:55 CST 2016]
-group1.statefulJob1 complete (2).
---group1.statefulJob1 executing.[Fri Feb 19 16:16:05 CST 2016]
-group1.statefulJob1 complete (3).
---group1.statefulJob1 executing.[Fri Feb 19 16:16:15 CST 2016]
-group1.statefulJob1 complete (4).
---group1.statefulJob1 executing.[Fri Feb 19 16:16:25 CST 2016]
-group1.statefulJob1 complete (5).
---group1.statefulJob1 executing.[Fri Feb 19 16:16:35 CST 2016]
-group1.statefulJob1 complete (6).
---group1.statefulJob1 executing.[Fri Feb 19 16:16:45 CST 2016]
-group1.statefulJob1 complete (7).
---group1.statefulJob1 executing.[Fri Feb 19 16:16:55 CST 2016]
-group1.statefulJob1 complete (8).
---group1.statefulJob1 executing.[Fri Feb 19 16:17:05 CST 2016]
-group1.statefulJob1 complete (9).
---group1.statefulJob1 executing.[Fri Feb 19 16:17:18 CST 2016]
-group1.statefulJob1 complete (10).
四、示例程序2
=====代碼=====
JobDetail job2 = newJob(StatefulDumbJob.class).withIdentity("statefulJob2", "group1") .usingJobData(StatefulDumbJob.EXECUTION_DELAY, 10000L).build(); SimpleTrigger trigger2 = newTrigger() .withIdentity("trigger2", "group1") .startAt(startTime) .withSchedule(simpleSchedule().withIntervalInSeconds(3).repeatForever() .withMisfireHandlingInstructionNowWithExistingCount()) // set misfire instructions .build(); Date ft2 = sched.scheduleJob(job2, trigger2); log.info(job2.getKey() + " will run at: " + ft2 + " and repeat: " + trigger2.getRepeatCount() + " times, every " + trigger2.getRepeatInterval() / 1000 + " seconds");
=====代碼=====
misfireThreshold設置為60000。
分析:
示例2與示例1的區別在於,觸發器指定了激活失敗指令為MISFIRE_INSTRUCTION_RESCHEDULE_NOW_WITH_EXISTING_COUNT。
各任務的延遲量如下表所示:
任務編號 |
預定運行時刻 |
實際運行時刻 |
延遲量(秒) |
備注 |
1 |
17:05:15 |
17:05:15 |
0 |
|
2 |
17:05:18 |
17:05:25 |
7 |
|
3 |
17:05:21 |
17:05:35 |
14 |
|
4 |
17:05:24 |
17:05:45 |
21 |
|
5 |
17:05:27 |
17:05:55 |
28 |
|
6 |
17:05:30 |
17:06:05 |
35 |
|
7 |
17:05:33 |
17:06:15 |
42 |
|
8 |
17:05:36 |
17:06:25 |
49 |
|
9 |
17:05:39 |
17:06:35 |
56 |
|
無 |
17:05:42 |
17:06:45 |
63 |
misfire |
10 |
17:06:45 |
17:06:45 |
0 |
|
11 |
17:05:48 |
17:06:55 |
7 |
|
在17:06:45發生misfire。根據其激活失敗指令,任務10在該時刻被立即執行,延遲量清零。然后,程序繼續如此循環下去。
程序輸出:
---group1.statefulJob2 executing.[Fri Feb 19 17:05:15 CST 2016]
-group1.statefulJob2 complete (1).
---group1.statefulJob2 executing.[Fri Feb 19 17:05:25 CST 2016]
-group1.statefulJob2 complete (2).
---group1.statefulJob2 executing.[Fri Feb 19 17:05:35 CST 2016]
-group1.statefulJob2 complete (3).
---group1.statefulJob2 executing.[Fri Feb 19 17:05:45 CST 2016]
-group1.statefulJob2 complete (4).
---group1.statefulJob2 executing.[Fri Feb 19 17:05:55 CST 2016]
-group1.statefulJob2 complete (5).
---group1.statefulJob2 executing.[Fri Feb 19 17:06:05 CST 2016]
-group1.statefulJob2 complete (6).
---group1.statefulJob2 executing.[Fri Feb 19 17:06:15 CST 2016]
-group1.statefulJob2 complete (7).
---group1.statefulJob2 executing.[Fri Feb 19 17:06:25 CST 2016]
-group1.statefulJob2 complete (8).
---group1.statefulJob2 executing.[Fri Feb 19 17:06:35 CST 2016]
-group1.statefulJob2 complete (9).
---group1.statefulJob2 executing.[Fri Feb 19 17:06:45 CST 2016]
-group1.statefulJob2 complete (10).
---group1.statefulJob2 executing.[Fri Feb 19 17:06:55 CST 2016]
-group1.statefulJob2 complete (11).