quartz問題記錄-missed their scheduled fire-time


這里有3個原因:
1.所有的woker thread(工作線程; 輔助線程)都在運行其他的job
2.scheduler(調度器)down了(關於這個down。我不太明確是shutdown了。。還是掛掉了。因此下文依舊用down。)
3.任務被安排在過去的某一時刻啟動(此可能為代碼錯誤)

我的

我的解決方法:修改quartz.properties文件中的org.quartz.threadPool.threadCount的值增大。(從原來的10增大到20),

      最近公司加了好多定時任務,導致線程不夠用,至此quartz掛掉。

 

 

以下來源於網上:

可以簡單地通過配置quartz.properties文件中的org.quartz.threadPool.threadCount,來增加worker thread的數量(默認為10)。但是當整個 application/server/scheduler (調度器)掛掉的時候,這方法仍然是無效的。這種Quartz無法啟動指定觸發器情況,叫做 misfire(http://fanyi.baidu.com/#en/zh/misfire 以下統一用misfire,漢語無法貼切表達)。
你知道當這種情況發生的時候,Quartz能做些什么嗎? 其實,Quartz有許多的策略(叫misfire instructions,失火指示)可以處理問題,並且當你沒有去想這方面問題的時候,它也有許多默認策略。但是為了使你的應用更加穩定和可預測(尤其是在高負載和可維護性上),你應該有意識的去確保triggers 和 jobs工作正常。

基於你用的trigger,會有不同的配置選項(對misfire instructions有效)。當然,不同的trigger也會使Quartz有不同的行為(叫做smart policy,明智的決策)。盡管文檔中有misfire instructions的描述,但是我發現都很難理解它所想表達的意思,因此,我寫下這篇簡短的總結。

 

在我深入細節之前,還有一個配置需要說明下:org.quartz.jobStore.misfireThreshold(毫秒級),默認是60000(一分鍾)。它定義了trigger被認為是misfired了的時限。

 

基於默認配置,如果trigger應該在30秒以前被觸發,那么很愉快地,Quartz就把它搞定了。這種延遲(delay)不能叫失火。

 

然而當trigger被發現,延遲了61秒時,那么專門的“失火處理者(misfire handler thread)”就會按照misfire instructions去處理它了。

 

 

 

為了測試效果,我們將這個時間設置為1000(即1秒),這樣就能很快的測試“失火”了。


 

第一個例子,是一個不需要重復觸發的普通trigger,我們來看看普通trigger調度器是怎么處理“失火”,並讓它運行一次的:

 

【原文:Simple trigger without repeating In our first example we will see how misfiring is handled by simple triggers scheduled to run only once:】

 

1. val trigger = newTrigger().
2. startAt(DateUtils.addSeconds(new Date(), -10)).
3. build()

 

同樣的trigger,但是明確設置了misfire instruction handler(失火處理者):

 

1. val trigger = newTrigger().
2. startAt(DateUtils.addSeconds(new Date(), -10)).
3. withSchedule(
4. simpleSchedule().
5. withMisfireHandlingInstructionFireNow() //MISFIRE_INSTRUCTION_FIRE_NOW
6. ).
7. build()

 

為了測試,我將trigger設置為10秒前被調度(即當創建后,就已經晚於啟動時間10秒)。在實際使用時,我們基本上永遠不會這么設置。

 

換句話說,如果我們正確的設置了trigger,但是當需要被調度的時候,調度器down了或者沒有空閑的worker thread了。那么,Quartz怎么處理這種extraordinary(罕見,古怪)的情況呢?

 

在第一段代碼中,沒有設置misfire instruction(so called smart policy is used in that case 這句不太會翻譯。。。。)。

 

第二段代碼中,明確指定了當misfire發生時,我們希望采取的行為。

 

來看下表:

 

 

 

指令 Instruction 意義 Meaning
smart policy - default See: withMisfireHandlingInstructionFireNow
withMisfireHandlingInstructionFireNow
MISFIRE_INSTRUCTION_FIRE_NOW
調度器發現misfire情況后,立即執行job。
這是smart policy。
例如:
你讓一些系統清理功能在2點執行。但是很不幸,應用在那段時間由於維護,關閉了,直到3點才恢復。這樣trigger就misfire了,然后調度器會嘗試修復這種情況,在3點啟動后,盡快執行。
withMisfireHandlingInstructionIgnoreMisfires
MISFIRE_INSTRUCTION_IGNORE_MISFIRE_POLICY
See: withMisfireHandlingInstructionFireNow
withMisfireHandlingInstructionNextWithExistingCount
MISFIRE_INSTRUCTION_RESCHEDULE_NEXT_WITH_EXISTING_COUNT
See: withMisfireHandlingInstructionNextWithRemainingCount
withMisfireHandlingInstructionNextWithRemainingCount
MISFIRE_INSTRUCTION_RESCHEDULE_NEXT_WITH_REMAINING_COUNT
什么都不做。misfire被忽略了,並且沒有后續的執行。當你想要徹底放棄被misfire的執行時,可以使用這個指令。
例如:
trigger是要啟動錄制一個電視節目。但是被misfire了,2個小時候,才發現。  
【PS: 這個不是太理解,只是按照原文翻譯過來,如果要用,請自行測試。。。】
withMisfireHandlingInstructionNowWithExistingCount
MISFIRE_INSTRUCTION_RESCHEDULE_NOW_WITH_EXISTING_REPEAT_COUNT
See: withMisfireHandlingInstructionFireNow
withMisfireHandlingInstructionNowWithRemainingCount
MISFIRE_INSTRUCTION_RESCHEDULE_NOW_WITH_REMAINING_REPEAT_COUNT
See: withMisfireHandlingInstructionFireNow

 

 

 

 

 

普通trigger重復執行指定次數。這種情形更加復雜。想象一下,我們有一些需要重復執行指定次數的job:

 

 

 

01. val trigger = newTrigger().
02. startAt(dateOf(900)).
03. withSchedule(
04. simpleSchedule().
05. withRepeatCount(7).
06. withIntervalInHours(1).
07. WithMisfireHandlingInstructionFireNow()  //or other
08. ).
09. build()

 

在這個例子中,trigger從今天9點開始(startAt(dateOf(9, 0, 0)),共觸發8次(第一次執行,和7次重復)。

 

按理,最后一次執行應該在下午4點被觸發。假設由於某些原因,在9點和10點調度器沒有執行job,並且直到10:15才被系統發現misfire,也就是misfire了2次。這種情況下,調度器會怎么樣呢?

 

 

 

指令 Instruction 意義 Meaning
smart policy - default See:withMisfireHandlingInstructionNowWithExistingCount
withMisfireHandlingInstructionFireNow
MISFIRE_INSTRUCTION_FIRE_NOW
See:withMisfireHandlingInstructionNowWithRemainingCount
withMisfireHandlingInstructionIgnoreMisfires
MISFIRE_INSTRUCTION_IGNORE_MISFIRE_POLICY
盡快啟動所有被啟動的trigger,並將調度置為正常。
例如:
在我們上面的例子中,調度器會立即執行9點和10點的任務,並等待11點時,繼續按正常的調度執行。
備注:當處理misfire時,我們同樣要注意到,實際job執行的時間,已經滯后於應該執行的時間。這意味着,你不能簡單地的依賴當前系統時間,而是應該使用 JobExecutionContext .getScheduledFireTime()去獲取。
1. def execute(context: JobExecutionContext) {
2. val date = context.getScheduledFireTime
3. //...
4. }
withMisfireHandlingInstructionNextWithExistingCount
MISFIRE_INSTRUCTION_RESCHEDULE_NEXT_WITH_EXISTING_COUNT
調度器不會馬上有反應。它會等待下一次執行,然后根據應該調度次數去運行trigger。
See also: withMisfireHandlingInstructionNextWithRemainingCount
例如:
在10點15發現2次misfire。調度器會等到11點,繼續執行,並會每小時執行1次,共執行8次調度操作,直到下午6點停止(本該4點停止的。)
withMisfireHandlingInstructionNextWithRemainingCount
MISFIRE_INSTRUCTION_RESCHEDULE_NEXT_WITH_REMAINING_COUNT
調度器會拋棄被misfire的操作,然后等待下次執行。這樣,執行的總次數,就會小於配置的次數。
例如:在10點15,2次misfire的執行都被丟棄了。調度器會等到下個執行時間-11點,然后繼續觸發其余的trigger,直到4點。事實上,這種情況就像misfire從未發生過一樣。
withMisfireHandlingInstructionNowWithExistingCount
MISFIRE_INSTRUCTION_RESCHEDULE_NOW_WITH_EXISTING_REPEAT_COUNT
第一次misfire的trigger會立即執行,而后會按設置的間隔,依次執行剩余的trigger。實際上,就像misfire的trigger的第一次觸發時間,被平移到了當前時間。
例如:
調度器會在10點15第一次運行misfire的trigger,然后隔1個小時,在11點15執行第二次。共執行8次,最后一次,在下午5點15。

withMisfireHandlingInstructionNowWithRemainingCount
MISFIRE_INSTRUCTION_RESCHEDULE_NOW_WITH_REMAINING_REPEAT_COUNT

第一次misfire的操作,被立即執行。其余被misfire的操作,會被拋棄。
剩余沒有被misfire的trigger,會按固定間隔被觸發。
例如:
調度器會在10點15運行第一次被misfire的操作(9點的。)。然后,它拋棄其余被misfire的(10點那一次)。最后,它會等1小時繼續觸發6個trigger:
11:15,12:15.... 4:15 PM。


 

 

這是一個基於指定間隔、並重復無數次的trigger:

 

 

 

01. val trigger = newTrigger().
02. startAt(dateOf(900)).
03. withSchedule(
04. simpleSchedule().
05. withRepeatCount(SimpleTrigger.REPEAT_INDEFINITELY).
06. withIntervalInHours(1).
07. WithMisfireHandlingInstructionFireNow()  //or other
08. ).
09. build()

 

trigger應該從今天9點開始(startAt(dateOf(9, 0, 0)),每隔小時觸發一次。然而調度器在9點到10點都沒有執行job(比如關閉了系統、線程不夠等等。。前面有介紹),並且在10點15時才被發現,misfire了2次。這種情況比那種執行執行次數的trigger更加普遍。

 

 

 

指令 Instruction 意義 Meaning
smart policy - default See:withMisfireHandlingInstructionNowWithExistingCount
withMisfireHandlingInstructionFireNow
MISFIRE_INSTRUCTION_FIRE_NOW
See: withMisfireHandlingInstructionNowWithRemainingCount
withMisfireHandlingInstructionIgnoreMisfires
MISFIRE_INSTRUCTION_IGNORE_MISFIRE_POLICY
https://jira.terracotta.org/jira/browse/QTZ-283
調度器會立即執行所有misfire的trigger,然后繼續正常調度。
例如:
9點和10點的trigger會立即執行,下次執行將按計划執行(下一次是11點執行)。

withMisfireHandlingInstructionNextWithExistingCount
MISFIRE_INSTRUCTION_RESCHEDULE_NEXT_WITH_EXISTING_COUNT
See:withMisfireHandlingInstructionNextWithRemainingCount
withMisfireHandlingInstructionNextWithRemainingCount
MISFIRE_INSTRUCTION_RESCHEDULE_NEXT_WITH_REMAINING_COUNT
不做任何事情,misfire的情況被忽略掉。然后,調度器按設置的間隔等待下次執行。
例如:
9點和10點misfire的執行被忽略掉。第一次執行會在11點會開始。
Example scenario: Misfired execution at 9 and 10 AM are discarded. The first execution occurs at 11 AM.

withMisfireHandlingInstructionNowWithExistingCount
MISFIRE_INSTRUCTION_RESCHEDULE_NOW_WITH_EXISTING_REPEAT_COUNT
See:withMisfireHandlingInstructionNowWithRemainingCount
withMisfireHandlingInstructionNowWithRemainingCount
MISFIRE_INSTRUCTION_RESCHEDULE_NOW_WITH_REMAINING_REPEAT_COUNT


第一次misfire的執行會被立即運行,其余的被忽略。下次執行會在設置的間隔時間后被觸發。實際上,就是第一次執行被推遲到了當前時間。
例如:
調度器在10點15立即執行misfire的trigger,然后等待一個小時后,在11點15時,執行第二次。以后會每隔一小時。


定時觸發    CRON trigger

 

定時觸發是Quartz中最常見的。它有另外兩個有效的trigger:DailyTimeIntervalTrigger(比如每25分鍾一次)和CalendarIntervalTrigger(比如5個月執行一次)。They support triggering policies not possible in both CRON and simple triggers.(不會譯- -!),但是他們和CRON trigger一樣,支持同樣的misfire handling instructions(失火處理指令)。

 

 

 

1. val trigger = newTrigger().
2. withSchedule(
3. cronSchedule("0 0 9-17 ? * MON-FRI").
4. withMisfireHandlingInstructionFireAndProceed()  //or other
5. ).
6. build()

 

這個例子是,trigger在每周一到周五的早上9點到下午5點間,每小時被觸發一次。但是兩次觸發被misfire了,並且在10點15時,才發現這個情況。請注意,他們的失火指令效果與普通trigger是不同的:

 

 

 

指令 Instruction 意義 Meaning
smart policy - default See: withMisfireHandlingInstructionFireAndProceed
withMisfireHandlingInstructionIgnoreMisfires
MISFIRE_INSTRUCTION_IGNORE_MISFIRE_POLICY
https://jira.terracotta.org/jira/browse/QTZ-283
所有被misfire的執行會被立即執行,然后按照正常調度繼續執行trigger。
例如:
9點和10點的執行(misfire的2個)被立即執行,下次執行將在11點被准時執行。
withMisfireHandlingInstructionFireAndProceed
MISFIRE_INSTRUCTION_FIRE_ONCE_NOW
立即執行第一次misfire的操作,並且放棄其他misfire的(類似所有misfire的操作被合並執行了)。然后繼續按調度執行。無論misfire多少次trigger的執行,都只會立刻執行1次。
例如:
9點和10點的被合並執行一次(換句話說,10點需要執行的那次,被pass了)。下次執行將在11點被准時執行。
withMisfireHandlingInstructionDoNothing
MISFIRE_INSTRUCTION_DO_NOTHING
所有被misfire的執行都被忽略掉,調度器會像平時一樣等待下次調度。
例如:
9點和10點的被忽略掉,好像什么都沒發生一樣。下次執行將在11點被執行。
   

 

QTZ-283NoteQTZ-283: MISFIRE_INSTRUCTION_IGNORE_MISFIRE_POLICY not working with JDBCJobStore - apparently there is a bug when JDBCJobStore is used, keep an eye on that issue. (在用JDBCJobStore 時,MISFIRE_INSTRUCTION_IGNORE_MISFIRE_POLICY 沒有生效。顯然,這是在使用JDBCJobStore時的一個bug,關注下。)

 

 

 

如你所有,根據實際的設定,不同的trigger會有不同的行為。此外,雖然它提供了smart policy(明智的決策),但是真正使用時,還是要取決於業務需求。

 

從本質上看,主要有三種策略:忽略,立即運行然后繼續正常執行,忽略misfire的並等待下次執行。( 原文: ignorerun immediately and continue and discard and wait for next. )

 

它們有不同的應用場景:

 

當你需要確保每次調度任務都要被執行的時候,即時它意味着多個misfire的trigger會被觸發,那么用ignore policies。試想一下,有一個任務,需要每小時,都根據上一小時的訂單去生成報表。如果服務被關閉了8個小時,那你可能仍然是盡快得到那些報表的。這種情況下,配置ignore policies,調度器會盡快將那8小時的調度任務運行一遍的。盡管晚了幾個小時,但是仍然是被執行了(最終報告到手了。^_^)。

 

當你需要任務被定期執行,並且當出現misfire的情況后立即運行一次的時候,那么使用now* policies。試想一下,一個任務是每分鍾清空文件夾 /tmp。如果調度器在20分鍾內繁忙,最后終於可以執行這個任務了,那么你肯定不會希望它執行20次的!一次就足夠了,但是要盡快執行。而后,再回到正常的執行間隔--1分鍾。

 

當你希望任務能在特定時間點運行的時候,使用next* policies不錯。比如你需要在每個整點后15分鍾抓取股票的價格。它們的變化非常快,然后現在已經整點后20分了,那么不必煩惱。你剛好錯過了5分鍾,但是現在你已經不在乎(那時候的價格)了。這時,一個時間間隙總好過一個不准確的值。這種情況Quartz只要跳過misfire的操作,等待下次執行就好了。

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM