2014-09-26 241 views
1

我們針對各種計劃運行Quartz.NET,計劃範圍從每30秒到每週一次。Quartz.NET - 計劃任務隨機停止運行

在回顧我們的內部採伐記錄時,我們發現有些作業已經停止運行,因爲沒有明顯的原因,即使其他作業仍在繼續。例如,我們每30秒鐘的工作在給定的時間內都失敗了,而每10分鐘的工作持續幾個小時,然後也失敗了。日後的任務稍後停止。

我們啓用了Quartz日誌記錄,發現以下內容。

LOG上一頁火災,這是成功的:

2014-09-19 08:20:00.0130 DEBUG Producing instance of Job 'DEFAULT.Scheduled task #5', class=TaskRunner 
2014-09-19 08:20:00.0130 DEBUG Calling Execute on job DEFAULT.Scheduled task #5 
2014-09-19 08:20:00.0130 DEBUG Batch acquisition of 1 triggers 
2014-09-19 08:20:00.8710 DEBUG Trigger instruction : NoInstruction 
2014-09-19 08:20:00.8710 DEBUG Batch acquisition of 1 triggers 

LOG第一次失敗的:

2014-09-19 08:30:00.0046 DEBUG Producing instance of Job 'DEFAULT.Scheduled task #5', class=TaskRunner 
2014-09-19 08:30:00.0046 DEBUG Calling Execute on job DEFAULT.Scheduled task #5 
2014-09-19 08:30:00.0046 DEBUG Batch acquisition of 1 triggers 

在此之後,直到我們重新啓動該服務這個特殊的工作永遠不會再次運行。沒有跡象表明我們的代碼是在這個特定的實例上運行的,因爲我們在內部執行了自己的日誌記錄,這在當時沒有發生。

我們的失火處理配置爲每一項工作如下:

 ... TriggerBuilder.Create() 
      .WithCronSchedule(task.CronSchedule, x => x.WithMisfireHandlingInstructionDoNothing()) 
      .Build(); 

我明白了「DoNothing」指令告訴它跳過這個火,繼續進行計劃。因此,如果發生失火,我預計它會在下次着火時間再次起火。

1)爲什麼我們的Quartz工作隨機失敗?

2)我們可以做些什麼來進一步調查?

回答

1

我明白「DoNothing」指令會告訴它跳過這條消息並繼續計劃。因此,如果發生失火,我預計它會在下次着火時間再次起火。

這是正確的。目前的執行將被拋棄。然而,它應該繼續無限的計劃,並在給定的時間創建一個新的執行,即使在以前的執行中有未處理的異常。

爲什麼我們的Quartz工作隨機失敗?

作業失誤的一些重要原因可能是沒有足夠的工作線程來處理作業(可以配置此作業)或調度程序本身已關閉。調度程序作業也可能因爲它們被設置爲在過去的時間開始而失效。

我們可以做些什麼來進一步調查?

我懷疑你沒有足夠的工作線程配置處理作業。您還應該確保不會阻塞較長時間的工作線程,因爲這可能導致工作線程池耗盡並導致失火。此外,如果您的cron時間表不是限制性的,您可以嘗試將失火設置設置爲WithMisfireHandlingInstructionFireAndProceed繼續觸發執行,直到它完成。

+0

謝謝,我們將執行此操作,看看它是否有幫助。 – 2014-09-30 22:16:51

2

使用來源,盧克! Quartz是開源的。所以挖在那裏!

搜索(「調用execute」)的日誌消息使我這個代碼(最新的源)在JobRunShell.cs:

// Execute the job 
try 
{ 
    if (log.IsDebugEnabled) 
    { 
     log.Debug("Calling Execute on job " + jobDetail.Key); 
    } 
      job.Execute(jec); 
    endTime = SystemTime.UtcNow(); 
} 
catch (JobExecutionException jee) 
{ 
    endTime = SystemTime.UtcNow(); 
    jobExEx = jee; 
    log.Info(string.Format(CultureInfo.InvariantCulture, "Job {0} threw a JobExecutionException: ", jobDetail.Key), jobExEx); 
} 
catch (Exception e) 
{ 
    endTime = SystemTime.UtcNow(); 
    log.Error(string.Format(CultureInfo.InvariantCulture, "Job {0} threw an unhandled Exception: ", jobDetail.Key), e); 
    SchedulerException se = new SchedulerException("Job threw an unhandled exception.", e); 
    qs.NotifySchedulerListenersError(
     string.Format(CultureInfo.InvariantCulture, "Job ({0} threw an exception.", jec.JobDetail.Key), se); 
    jobExEx = new JobExecutionException(se, false); 
} 
jec.JobRunTime = endTime - startTime; 

// notify all job listeners 
if (!NotifyJobListenersComplete(jec, jobExEx)) 
{ 
    break; 
} 
instCode = SchedulerInstruction.NoInstruction; 
// update the trigger 
try 
{ 
    instCode = trigger.ExecutionComplete(jec, jobExEx); 
    if (log.IsDebugEnabled) 
    { 
     log.Debug(string.Format(CultureInfo.InvariantCulture, "Trigger instruction : {0}", instCode)); 
    } 
} 
catch (Exception e) 
{ 
    // If this happens, there's a bug in the trigger... 
    SchedulerException se = new SchedulerException("Trigger threw an unhandled exception.", e); 
    qs.NotifySchedulerListenersError("Please report this error to the Quartz developers.", se); 
} 

所以,看你的輸出,我們看到上線日誌消息在上面的代碼示例中爲6。但是,我們從來沒有看到觸發器清除(第3行到最後一行)輸出。

請注意,在該代碼的每個catch語句中,我們正在創建調度程序異常並通知偵聽器?

那麼,選擇很明確:當你新增一個新的SchedulerListener到你的Quartz調度器(用你自己的類實現ISchedulerListener),然後監聽調度器異常並記錄錯誤。 SchedulerException封裝了原始異常,因此您應該可以訪問其中的基礎錯誤。

順便說一句...所有的代碼片段是在另一個嘗試塊......但沒有一個catch塊。如果你仍然無法找到正在發生的事情,那麼在這個函數上添加一個全局的catch,並在文件的其他地方執行它(在SchedulerException中包裝異常並通知監聽器)。

+0

這有很多幫助,我們很快會發現這是否揭示了答案。 – 2014-10-02 23:35:10

+0

我們還沒有孤立這個問題,但是這篇文章爲我們提供了最有用的診斷工具來繼續我們的調查,所以賞金就在這裏。 – 2014-10-06 04:06:04

0

在過去,我有和你一樣的問題。我沒有任何關於錯誤起源的線索。無論如何,我修復它採取以下措施:

1-)減少工作工作的minimun。我使用MSMQ排隊JobExecution並且QUARTZ排程只向隊列中添加一條新消息(這爲我解決了所有問題)

2-)您可以在作業中避免像線程同步這樣的事情。 3)其他可以避免的修復是QUARTZ版本更新。我從2.1.2升級到2.2.1時開始面臨這個問題

希望這對你有所幫助!