【踩坑記錄】Quartz避坑指南,看這一篇就夠了。。。

一、基本組件介紹

  • Scheduler 任務調度器,按照特定的觸發規則,自動執行任務。
  • Job 接口,定義需要執行的任務。
  • JobDetail 包含job的基本信息。
  • Trigger 描述Job執行的時間觸發規則。
  • JobStore 存放Job、Trigger等信息。

Scheduler

Scheduler是一個任務調度器,保存JobDetail和Trigger的信息。 在Trigger觸發時,執行特定任務。

實現了org.quartz.Scheduler接口的StdSchedule實際只是QuartzScheduler的代理,後者實現了Scheduler所有操作。

Schedualer體系

 

創建

Scheduler由SchedulerFactory創建。
SchedulerFactory有兩個默認實現StdSchedulerFactoryDirectSchedulerFactory

Schedualer Factory繼承體系

Scheduler的創建過程包括:

  • 讀取配置文件, 配置文件中需要配置scheduler、線程池、jobStore、jobListener、triggerListenner、插件等。
  • 配置文件的讀取過程如下:
    • 讀取參數系統參數System中配置的org.quartz.properties指定的文件
    • 如果找不到則讀取項目Classpath目錄下的quartz.properties配置文件
    • 如果找不到則讀取quartz jar包中默認的配置文件quartz.properties
    • 從SchedulerRepository中根據名稱讀取已經創建的scheduler,
    • 如果沒有則重新創建一個,並保存在SchedulerRepository中。

存儲

Scheduler存儲在單例的SchedulerRepository中。

Schedualer Repository類圖

生命週期

Scheduler的生命週期開始於其被創建時,結束於shutdown()方法調用。一旦對象創建完成,就可以用來操作Jobs和Triggers,包括添加、刪除、查詢等。但只有在Scheduler start()被調用後,纔會按照Trigger定義的觸發規則執行Job的內容。

Schedualer 生命週期

 

核心方法

Scheduler的核心功能就是操作Job、Trigger、Calendar、Listener等。包括addXXX、deleteXXX、pauseXXX、resumeXXX等。

Schedualer 核心方法

 

Job

Job接口簡介

Job就是定時任務實實在在執行的內容,足夠單純,僅僅包含一個執行方法。

Job

JobExecutionContext對象包含了當前任務執行的上下文環境,包括JobDetail、Trigger以及jobDataMap等。

Job運行時環境

Job的執行並不是孤立封閉的,需用與外界交互。JobDataMap是一種擴展的Map<String,Object>結構,就是用來在任務調度器與任務執行之間傳遞數據。如果Job中包含了與JobDataMap中key值相對應的setter方法,那麼Scheduler容器將會在當前Job創建後自動調用該setter方法,完成數據傳遞,而不用hardcode的從map中取值。
Scheduler控制在每次Trigger觸發時創建Job實例。因此JobExecutionContext.JobDataMap只是外部Scheduler容器中JobDataMap的一個拷貝,即便修改Job中的JobDataMap也只是在當前Job執行的環境中生效,並不會對外部產生任何影響。

Job的派生

Job下面又派生出兩個子接口:InterruptableJob和StatefulJob

Job體系結構

InterruptableJob:可被阻斷的Job,InterruptableJob收到Scheduler.interrupt請求,停止任務
StatefulJob:有狀態Job,標識性接口,沒有操作方法。StatefulJob與普通的Job(無狀態Job)從根本上有兩點不同:
1. JobDataMap是共享的,即在Job中對JobDataMap的操作,將會被保存下來,其他Job拿到的將是被修改過的JobDataMap。
2. 基於第一條原因,StatefulJob是不允許併發執行的。
StatefulJob已被DisallowConcurrentExecution/PersistJobDataAfterExecution註解取代

Job的創建

Job的創建由專門的工廠來完成

Job Factory結構

Job是在Quartz內部創建,受Scheduler控制,因此不需要外部參與。

JobDetail

JobDetail用於保存Job相關的屬性信息

JobDetail結構

  • JobKey唯一確定了一個Job
  • JobDataMap用於存儲Job執行時必要的業務信息
  • JobDetail保存的僅僅是Job接口的Class對象,而非具體的實例。
    JobBuilder負責JobDetail實例的創建,並且JobBuilder提供了鏈式操作,可以方便的爲JobDetail添加額外的信息。

 

JobDetail job = JobBuilder.newJob(HelloJob.class)
                    .withIdentity(jobKey)
                    .build();

Trigger

Trigger描述了Job的觸發規則。

Trigger

  • TriggerKey(group,name)唯一標識了Scheduler中的Trigger
  • JobKey指向了該Trigger作用的Job
  • 一個Trigger僅能對應一個Job,而一個Job可以對應多個Trigger
  • 同樣的,Trigger也擁有一個JobDataMap
  • Priority:當多個trigger擁有相同的觸發時間,根據該屬性來確定先後順序,數值越大越靠前,默認5,可爲正負數
  • Misfire Instructions:沒來得及執行的機制。同一時間trigger數量過多超過可獲得的線程資源,導致部分trigger無法執行。不同類型的Trigger擁有不同的機制。當Scheduler啓動時候,首先找到沒來得及執行的trigger,再根據不同類型trigger各自的處理策略處理
  • Calendar:Quartz Calendar類型而不是java.util.Calendar類型。用於排除Trigger日程表中的特定時間範圍,比如原本每天執行的任務,排除非工作日

Trigger的幾種狀態

  • STATE_WAITING(默認): 等待觸發
  • STATE_ACQUIRED:
  • STATE_COMPLETE:
  • STATE_PAUSED:
  • STATE_BLOCKED:
  • STATE_PAUSED_BLOCKED:
  • STATE_ERROR:

Trigger的分類

Trigger的分類

常見的兩種Trigger爲SimpleTrigger和CronTrigger.

SimpleTrigger

SimpleTrigger支持在特定時間點一次性執行或延遲執行N次,使用TriggerBuilder和SimpleScheduleBuilder創建
SimpleTrigger包含的屬性爲:

  • startTime 開始時間
  • endTime 如果指定的話,將會覆蓋repeat count
  • repeat count 重複次數 >=0 int
  • repeat interval 時間間隔(毫秒) >=0 long

CronTrigger

CronTrigger支持多次重複性複雜情況,支持Cron表達式,使用TriggerBuilder和CronScheduleBuilder創建。
Cron表達式由7部分組成,分別是秒 分 時 日期 月份 星期 年(可選),空格間隔。

字段 允許值 允許的特殊字符
0-59 , - * /
0-59 , - * /
小時 0-23 , - * /
日期 1-31 , - * ? / L W C
月份 1-12,JAN-DEC , - * /
星期 1-7,SUN-SAT , - * ? / L C #
年(可選) 留空,1970-2099 , - * /

特殊字符含義

  • "*": 代表所有可能的值
  • "/": 用來指定數值的增量, 在子表達式(分鐘)裏的0/15表示從第0分鐘開始,每15分鐘;在子表達式(分鐘)裏的"3/20"表示從第3分鐘開始,每20分鐘(它和"3,23,43")的含義一樣
  • "?": 僅被用於天和星期兩個子表達式,表示不指定值。當2個子表達式其中之一被指定了值以後,爲了避免衝突,需要將另一個子表達式的值設爲"?"
  • "L": 僅被用於天和星期兩個子表達式,它是單詞"last"的縮寫。如果在“L”前有具體的內容,它就具有其他的含義了。例如:"6L"表示這個月的倒數第6天,"FRI L"表示這個月的最後一個星期五
  • 'W' 可用於“日”字段。用來指定歷給定日期最近的工作日(週一到週五) 。比如你將“日”字段設爲"15W",意爲: "離該月15號最近的工作日"。因此如果15號爲週六,觸發器會在14號即週五調用。如果15號爲週日, 觸發器會在16號也就是週一觸發。如果15號爲週二,那麼當天就會觸發。然而如果你將“日”字段設爲"1W", 而一號又是週六, 觸發器會於下週一也就是當月的3號觸發,因爲它不會越過當月的值的範圍邊界。'W'字符只能用於“日”字段的值爲單獨的一天而不是一系列值的時候
  • 'L'和'W'可以組合用於“日”字段表示爲'LW',意爲"該月最後一個工作日"。
  • '#' 字符可用於“周幾”字段。該字符表示“該月第幾個周×”,比如"6#3"表示該月第三個週五( 6表示週五而"#3"該月第三個)。再比如: "2#1" = 表示該月第一個週一而 "4#5" = 該月第五個週三。注意如果你指定"#5"該月沒有第五個“周×”,該月是不會觸發的。
  • 'C' 字符可用於“日”和“周幾”字段,它是"calendar"的縮寫。 它表示爲基於相關的日曆所計算出的值(如果有的話)。如果沒有關聯的日曆, 那它等同於包含全部日曆。“日”字段值爲"5C"表示"日曆中的第一天或者5號以後",“周幾”字段值爲"1C"則表示"日曆中的第一天或者週日以後"。
    表達式舉例
  • "0 0 12 * * ?" 每天中午12點觸發
  • "0 15 10 ? * *" 每天上午10:15觸發
  • "0 15 10 * * ?" 每天上午10:15觸發
  • "0 15 10 * * ? *" 每天上午10:15觸發
  • "0 15 10 * * ? 2005" 2005年的每天上午10:15觸發
  • "0 * 14 * * ?" 在每天下午2點到下午2:59期間的每1分鐘觸發
  • "0 0/5 14 * * ?" 在每天下午2點到下午2:55期間的每5分鐘觸發
  • "0 0/5 14,18 * * ?" 在每天下午2點到2:55期間和下午6點到6:55期間的每5分鐘觸發
  • "0 0-5 14 * * ?" 在每天下午2點到下午2:05期間的每1分鐘觸發
  • "0 10,44 14 ? 3 WED" 每年三月的星期三的下午2:10和2:44觸發
  • "0 15 10 ? * MON-FRI" 週一至週五的上午10:15觸發
  • "0 15 10 15 * ?" 每月15日上午10:15觸發
  • "0 15 10 L * ?" 每月最後一日的上午10:15觸發
  • "0 15 10 ? * 6L" 每月的最後一個星期五上午10:15觸發
  • "0 15 10 ? * 6L 2002-2005" 2002年至2005年的每月的最後一個星期五上午10:15觸發
  • "0 15 10 ? * 6#3" 每月的第三個星期五上午10:15觸發

定時器正則表達式驗證
秒:^(\\*|[0-5]?[0-9]([,|\\-|\\/][0-5]?[0-9])*)$
分:^(\\*|[0-5]?[0-9]([,|\\-|\\/][0-5]?[0-9])*)$
時:^(\\*|([0-1]?[0-9]?|2[0-3])([,|\\-|\\/]([0-1]?[0-9]|2[0-3]))*)$
日期:^(\\*|\\?|(([1-9]|[1-2][0-9]|3[0-1])([,|\-|\/]([1-9]|[1-2][0-9]|3[0-1]))*)[CLW]?|[CLW]|LW)$
月份:^((\\*|[1-9]|(1[0-2]))([,|\-|\/]([1-9]|(1[0-2])))*)$
星期:^(\\*|L|\\?|[1-7](([,|\-|\/|\#][1-7])*|[LC]))$
年:^(\\*?|2[0-9]{3}([,|\-|\/]2[0-9]{3})*)$

Job Store

Job Store用於保存jobs, triggers等對應數據。JobStore的配置應在Quartz的配置文件中配置,代碼中應該避免直接操作JobStore實例
JobStroe的實現包括:

  • RAMJobStore:把所有數據保存在內容中,速度快但不能持久化。配置org.quartz.jobStore.class = org.quartz.simpl.RAMJobStore
  • JobStoreSupport:通過jdbc,以數據庫作爲存儲媒介
    • JobStoreCMT: 使用應用服務器提供事務管理機制
    • JobStoreTX: 不依賴與外部容器,可以自己提交、回滾事務

JobStore

RAMJobStore

RAMJobStore

JobDetail的存儲載體

JobWrapper

 

JobWrapper

Trigger的存儲載體

TriggerWrapper

 

// 存儲所有的TriggerWrapper
ArrayList<TriggerWrapper> triggers
// 以Trigger的group爲key,存儲TriggerKey<->TriggerWrapper形式的Map結構的Map
HashMap<String, HashMap<TriggerKey, TriggerWrapper>> triggersByGroup
// 以TriggerKey爲Key,存儲TriggerWrapper
HashMap<TriggerKey, TriggerWrapper> triggersByKey
// 即將被觸發的Trigger
TreeSet<TriggerWrapper> timeTriggers

TriggerWrapper

二、觸發器超時後的處理策略

2.1 背景

調度框架不能保證任務的百分百定時執行,所有經過某些原因,任務錯過默認的觸發時間時,需要有一定的策略去處理。

2.2 關鍵屬性misfireThreshold

在配置quartz.properties有這麼一個屬性就是misfireThreshold,用來指定調度引擎設置觸發器超時的"臨界值"。

要弄清楚觸發器超時臨界值的意義,那麼就要先弄清楚什麼是觸發器超時?打個比方:比如調度引擎中有5個線程,然後在某天的下午2點 有6個任務需要執行,那麼由於調度引擎中只有5個線程,所以在2點的時候會有5個任務會按照之前設定的時間正常執行,有1個任務因爲沒有線程資源而被延遲執行,這個就叫觸發器超時。

那麼超時的時間又是如何計算的呢?還接着上面的例子說,比如一個(任務A)應該在2點的時候執行,但是在2點的時候調度引擎中的可用線程都在忙碌狀態中,或者調度引擎掛了,這都有可能發生,然後再2點05秒的時候恢復(有可用線程或者服務器重新啓動),也就是說(任務A)應該在2點執行 但現在晚了5秒鐘。那麼這5秒鐘就是任務超時時間,或者叫做觸發器(Trigger)超時時間。

理解了上面的內容再來看misfireThreshold值的意義,misfireThreshold是用來設置調度引擎對觸發器超時的忍耐時間,簡單來說 假設misfireThreshold=6000(單位毫秒)。

那麼它的意思說當一個觸發器超時時間如果大於misfireThreshold的值 就認爲這個觸發器真正的超時(也叫Misfires)。

如果一個觸發器超時時間 小於misfireThreshold的值, 那麼調度引擎則不認爲觸發器超時。也就是說調度引擎可以忍受這個超時的時間。

還是 任務A 比它應該正常的執行時間晚了5秒 那麼misfireThreshold的值是6秒,那麼調度引擎認爲這個延遲時間可以忍受,所以不算超時(Misfires),那麼引擎會按照正常情況執行該任務。 但是如果 任務A  比它應該正常的執行時間晚了7秒 或者是6.5秒 只要大於misfireThreshold  那麼調度引擎就會認爲這個任務的觸發器超時。

2.3 觸發器超時後的處理策略

2.3.1我們在定義一個任務的觸發器時 最常用的就是倆種觸發器:1、SimpleTrigger 2、CronTrigger。

1、SimpleTrigger 默認的策略是 Trigger.MISFIRE_INSTRUCTION_SMART_POLICY 官方的解釋如下:

Instructs the Scheduler that upon a mis-fire situation, the updateAfterMisfire() method will be called on the Trigger to determine the mis-fire instruction, which logic will be trigger-implementation-dependent.

大意是:指示調度引擎在MisFire的狀態下,會去調用觸發器的updateAfterMisfire的方法來確定它的超時處理策略,裏面的邏輯取決於具體的實現類。

那我們在看一下updateAfterMisfire方法的說明:

If the misfire instruction is set to MISFIRE_INSTRUCTION_SMART_POLICY, then the following scheme will be used: 
If the Repeat Count is 0, then the instruction will be interpreted as MISFIRE_INSTRUCTION_FIRE_NOW.
If the Repeat Count is REPEAT_INDEFINITELY, then the instruction will be interpreted as MISFIRE_INSTRUCTION_RESCHEDULE_NEXT_WITH_REMAINING_COUNT. WARNING: using MISFIRE_INSTRUCTION_RESCHEDULE_NEXT_WITH_REMAINING_COUNT with a trigger that has a non-null end-time may cause the trigger to never fire again if the end-time arrived during the misfire time span.
If the Repeat Count is > 0, then the instruction will be interpreted as MISFIRE_INSTRUCTION_RESCHEDULE_NOW_WITH_EXISTING_REPEAT_COUNT.

大意是:

1、如果觸發器的重複執行數(Repeat Count)等於0,那麼會按這個(MISFIRE_INSTRUCTION_FIRE_NOW)策略執行。

2、如果觸發器的重複執行次數是 SimpleTrigger.REPEAT_INDEFINITELY (常量值爲-1,意思是重複無限次) ,那麼會按照MISFIRE_INSTRUCTION_RESCHEDULE_NEXT_WITH_REMAINING_COUNT策略執行。

3、如果觸發器的重複執行次數大於0,那麼按照 MISFIRE_INSTRUCTION_RESCHEDULE_NOW_WITH_EXISTING_REPEAT_COUNT執行。

 

既然是這樣,那就讓我們依次看一下每種處理策略都是啥意思!

1、MISFIRE_INSTRUCTION_FIRE_NOW

Instructs the Scheduler that upon a mis-fire situation, the SimpleTrigger wants to be fired now by Scheduler.

NOTE: This instruction should typically only be used for 'one-shot' (non-repeating) Triggers. If it is used on a trigger with a repeat count > 0 then it is equivalent to the instruction MISFIRE_INSTRUCTION_RESCHEDULE_NOW_WITH_REMAINING_REPEAT_COUNT . 

指示調度引擎在MisFire的情況下,將任務(JOB)馬上執行一次。

需要注意的是 這個指令通常被用做只執行一次的Triggers,也就是沒有重複的情況(non-repeating),如果這個Triggers的被安排的執行次數大於0

那麼這個執行與 (4)MISFIRE_INSTRUCTION_RESCHEDULE_NOW_WITH_REMAINING_REPEAT_COUNT 相同

 

2、MISFIRE_INSTRUCTION_RESCHEDULE_NOW_WITH_EXISTING_REPEAT_COUNT

 

 

Instructs the Scheduler that upon a mis-fire situation, the SimpleTrigger wants to be re-scheduled to 'now' with the repeat count left as-is. This does obey the Trigger end-time however, so if 'now' is after the end-time the Trigger will not fire again.

 

NOTE: Use of this instruction causes the trigger to 'forget' the start-time and repeat-count that it was originally setup with (this is only an issue if you for some reason wanted to be able to tell what the original values were at some later time).

指示調度引擎重新調度該任務,repeat count保持不變,並且服從trigger定義時的endTime,如果現在的時間,如果當前時間已經晚於 end-time,那麼這個觸發器將不會在被激發。

注意:這個狀態會導致觸發器忘記最初設置的 start-time 和 repeat-count,爲什麼這個說呢,看源代碼片段:updateAfterMisfire方法中

1

2

3

4

5

6

7

8

9

10

11

12

13

else if (instr == MISFIRE_INSTRUCTION_RESCHEDULE_NOW_WITH_EXISTING_REPEAT_COUNT) {

    Date newFireTime = new Date();

    if (repeatCount != 0 && repeatCount != REPEAT_INDEFINITELY) {

        setRepeatCount(getRepeatCount() - getTimesTriggered());

        setTimesTriggered(0);

    }

     

    if (getEndTime() != null && getEndTime().before(newFireTime)) {

        setNextFireTime(null); // We are past the end time

    else {

        setStartTime(newFireTime);

        setNextFireTime(newFireTime);

    }

getTimesTriggered的是獲取這個觸發器已經被觸發了多少次,那麼用原來的次數 減掉 已經觸發的次數就是還要觸發多少次

接下來就是判斷一下觸發器是否到了結束時間,如果到了的話,觸發器就不會在被觸發。

然後就是重新設置觸發器的開始實現是 “現在” 並且立即運行。

3、MISFIRE_INSTRUCTION_RESCHEDULE_NEXT_WITH_REMAINING_COUNT

1

2

3

Instructs the Scheduler that upon a mis-fire situation, the SimpleTrigger wants to be re-scheduled to the next scheduled time after 'now' - taking into account any associated Calendar, and with the repeat count set to what it would be, if it had not missed any firings.

 

NOTE/WARNING: This instruction could cause the Trigger to go directly to the 'COMPLETE' state if all fire-times where missed.

這個策略跟上面的2策略一樣,唯一的區別就是設置觸發器的時間 不是“現在” 而是下一個 scheduled time。解釋起來比較費勁,舉個例子就能說清楚了。

比如一個觸發器設置的時間是 10:00 執行 時間間隔10秒 重複10次。那麼當10:07秒的時候調度引擎可以執行這個觸發器的任務。那麼如果是策略(2),那麼任務會立即運行。

那麼觸發器的觸發時間就變成了 10:07  10:17 10:27 10:37 .....

那麼如果是策略(3),那麼觸發器會被安排在下一個scheduled time。 也就是10:20觸發。 然後10:30 10:40 10:50。這回知道啥意思了吧。

 

4、MISFIRE_INSTRUCTION_RESCHEDULE_NOW_WITH_REMAINING_REPEAT_COUNT

這個策略跟上面的策略(2)比較類似,指示調度引擎重新調度該任務,repeat count 是剩餘應該執行的次數,也就是說本來這個任務應該執行10次,但是已經錯過了3次,那麼這個任務就還會執行7次。

下面是這個策略的源碼,主要看紅色的地方就能看到與策略(2)的區別,這個任務的repeat count 已經減掉了錯過的次數。

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

else if (instr == MISFIRE_INSTRUCTION_RESCHEDULE_NOW_WITH_REMAINING_REPEAT_COUNT) {

    Date newFireTime = new Date();

 

    int timesMissed = computeNumTimesFiredBetween(nextFireTime,

            newFireTime);

 

    if (repeatCount != 0 && repeatCount != REPEAT_INDEFINITELY) {

        int remainingCount = getRepeatCount()

                - (getTimesTriggered() + timesMissed);

        if (remainingCount <= 0) {

            remainingCount = 0;

        }

        setRepeatCount(remainingCount);

        setTimesTriggered(0);

    }

 

    if (getEndTime() != null && getEndTime().before(newFireTime)) {

        setNextFireTime(null); // We are past the end time

    else {

        setStartTime(newFireTime);

        setNextFireTime(newFireTime);

    }

}

  

5、MISFIRE_INSTRUCTION_RESCHEDULE_NEXT_WITH_REMAINING_COUNT

1

2

3

Instructs the Scheduler that upon a mis-fire situation, the SimpleTrigger wants to be re-scheduled to the next scheduled time after 'now' - taking into account any associated Calendar, and with the repeat count set to what it would be, if it had not missed any firings.

 

NOTE/WARNING: This instruction could cause the Trigger to go directly to the 'COMPLETE' state if all fire-times where missed.

這個策略與上面的策略3比較類似,區別就是repeat count 是剩餘應該執行的次數而不是全部的執行次數。比如一個任務應該在2:00執行,repeat count=5,時間間隔5秒, 但是在2:07才獲得執行的機會,那任務不會立即執行,而是按照機會在2點10秒執行。

 

6、MISFIRE_INSTRUCTION_IGNORE_MISFIRE_POLICY

 

 

Instructs the Scheduler that the Trigger will never be evaluated for a misfire situation, and that the scheduler will simply try to fire it as soon as it can, and then update the Trigger as if it had fired at the proper time.

NOTE: if a trigger uses this instruction, and it has missed several of its scheduled firings, then several rapid firings may occur as the trigger attempt to catch back up to where it would have been. For example, a SimpleTrigger that fires every 15 seconds which has misfired for 5 minutes will fire 20 times once it gets the chance to fire.

這個策略是忽略所有的超時狀態,和最上面講到的 (第二種情況) 一致。

舉個例子,一個SimpleTrigger  每個15秒鐘觸發, 但是超時了5分鐘才獲得執行的機會,那麼這個觸發器會被快速連續調用20次, 追上前面落下的執行次數。

 

2.3.2、CronTrigger 的默認策略也是Trigger.MISFIRE_INSTRUCTION_SMART_POLICY

官方解釋如下,也就是說不指定的話默認爲:MISFIRE_INSTRUCTION_FIRE_ONCE_NOW。

1

2

3

4

Updates the CronTrigger's state based on the MISFIRE_INSTRUCTION_XXX that was selected when the CronTrigger was created.

 

If the misfire instruction is set to MISFIRE_INSTRUCTION_SMART_POLICY, then the following scheme will be used:

The instruction will be interpreted as MISFIRE_INSTRUCTION_FIRE_ONCE_NOW

1、MISFIRE_INSTRUCTION_FIRE_ONCE_NOW

1

Instructs the Scheduler that upon a mis-fire situation, the CronTrigger wants to be fired now by Scheduler.

這個策略指示觸發器超時後會被立即安排執行,看源碼,紅色標記的地方。也就是說不管這個觸發器是否超過結束時間(endTime) 首選執行一次,然後就按照正常的計劃執行。

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

@Override

public void updateAfterMisfire(org.quartz.Calendar cal) {

    int instr = getMisfireInstruction();

 

    if(instr == Trigger.MISFIRE_INSTRUCTION_IGNORE_MISFIRE_POLICY)

        return;

 

    if (instr == MISFIRE_INSTRUCTION_SMART_POLICY) {

        instr = MISFIRE_INSTRUCTION_FIRE_ONCE_NOW;

    }

 

    if (instr == MISFIRE_INSTRUCTION_DO_NOTHING) {

        Date newFireTime = getFireTimeAfter(new Date());

        while (newFireTime != null && cal != null

                && !cal.isTimeIncluded(newFireTime.getTime())) {

            newFireTime = getFireTimeAfter(newFireTime);

        }

        setNextFireTime(newFireTime);

    else if (instr == MISFIRE_INSTRUCTION_FIRE_ONCE_NOW) {

        setNextFireTime(new Date());

    }

}

2、MISFIRE_INSTRUCTION_DO_NOTHING

這個策略與策略(1)正好相反,它不會被立即觸發,而是獲取下一個被觸發的時間,並且如果下一個被觸發的時間超出了end-time 那麼觸發器就不會被執行。

上面綠色標記的地方是源碼

補充幾個方法的說明:

1、getFireTimeAfter 返回觸發器下一次將要觸發的時間,如果在給定(參數)的時間之後,觸發器不會在被觸發,那麼返回null。

1

2

Date getFireTimeAfter(Date afterTime)

Returns the next time at which the Trigger will fire, after the given time. If the trigger will not fire after the given time, null will be returned.

2、isTimeIncluded 判斷給定的時間是否包含在quartz的日曆當中,因爲quartz是可以自定義日曆的,設置哪些日子是節假日什麼的。

1

2

boolean isTimeIncluded(long timeStamp)

Determine whether the given time (in milliseconds) is 'included' by the Calendar.

三、踩坑記錄

     Scheduler不要使用@Autowired自動注入,要使用創建StdSchedulerFactory類對象進行獲取,因爲自動注入的Scheduler的misfireThreshold默認是5000ms,而通過StdSchedulerFactory類對象獲取的Scheduler,misfireThreshold的值是60000ms。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章