2

我们以不同的时间表运行 Quartz.NET 来处理从每 30 秒到每周一次的各种作业。

在查看我们的内部日志记录时,我们发现一些作业无缘无故停止运行,而其他作业仍在继续运行。例如,我们的每 30 秒作业在给定时间失败,而另一个每 10 分钟作业持续了几个小时,然后也失败了。日常任务后来停止了。

我们启用了 Quartz 日志记录并发现以下内容。

以前成功的火灾记录:

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

第一次失败的日志:

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)我们可以做些什么来进一步调查?

4

3 回答 3

5

使用来源,卢克!Quartz 是开源的。所以挖进去!

搜索该日志消息(“调用执行”)将我带到 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 语句中,我们都在创建调度程序异常并通知侦听器?

好吧,选择很明确:在你新建 Quartz 调度器时添加一个新的 SchedulerListener(ISchedulerListener用你自己的类实现),然后监听调度器异常,并记录错误。SchedulerException 包装了原始异常,因此您应该可以访问其中的底层错误。

顺便说一句...所有这些代码片段都在另一个 try 块中...但没有 catch 块。如果你仍然找不到发生了什么,那么在这个函数上添加一个全局 catch 并在文件的其他地方执行它们所做的事情(将异常包装在 SchedulerException 中并通知侦听器)。

于 2014-10-01T18:07:14.717 回答
2

我了解“DoNothing”指令告诉它跳过这场火灾并继续执行计划。因此,如果发生失火,我希望它在下一次开火时再次开火。

那是对的。当前执行将被取消。然而,它应该无限地继续 cron 计划,并在给定时间创建一个新的执行,即使在以前的执行中存在未处理的异常。

为什么我们的 Quartz 作业会随机失败?

作业失败的一些关键原因可能是没有足够的工作线程来处理作业(可以配置)或调度程序本身已关闭。调度程序作业也可能会失败,因为它们被设置为在过去的时间开始。

我们可以做些什么来进一步调查?

我怀疑您没有配置足够的工作线程来处理这些作业。您还应该确保您不会长时间阻塞工作线程,因为这可能会导致工作线程池耗尽并导致失火。

此外,如果您的 cron 计划没有限制,您可以尝试将 misfire 设置设置为WithMisfireHandlingInstructionFireAndProceed以继续触发执行,直到它通过。

于 2014-09-29T13:35:41.403 回答
0

过去我和你有同样的问题。我对错误的起源没有任何线索。无论如何,我采取以下措施修复它:

1-) 将工作量减少到最低限度。我使用 MSMQ 将 JobExecution 排入队列,而 QUARTZ 计划仅将一条新消息放入队列(这为我解决了所有问题)

2-)您可以避免在作业中进行线程同步之类的事情。

3-) 您可以避免的其他修复是 QUARTZ 版本更新。从 2.1.2 升级到 2.2.1 时我开始面临这个问题

希望这对你有帮助!

于 2014-10-05T18:12:10.843 回答