我们为不同的时间表运行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)我们可以做些什么来进一步调查?
答案 0 :(得分:3)
使用来源,卢克! 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语句中,我们都在创建调度程序异常并通知侦听器?
嗯,选择很明确:在新建时向Quartz调度程序添加一个新的SchedulerListener(用你自己的类实现ISchedulerListener
),然后监听调度程序异常,并记录错误。 SchedulerException包装原始异常,因此您应该可以访问其中的基础错误。
BTW ......所有代码片段都在另一个try块中......但没有catch块。如果你仍然无法找到正在发生的事情,那么就在这个函数上添加一个全局catch并在文件的其他地方执行它们的操作(在SchedulerException中包装异常并通知侦听器)。
答案 1 :(得分:2)
我理解" DoNothing"指令告诉它跳过此火并继续计划。因此,如果发生熄火,我希望它会在下一次起火时再次发射。
这是正确的。目前的执行将被削减。但是,它应该无限地继续使用cron调度,并在给定时间创建新执行,即使先前执行中存在未处理的异常。
为什么我们的Quartz作业会在随机时间失败?
作业失误的一些关键原因可能是没有足够的工作线程来处理作业(可以配置)或者调度程序本身已关闭。调度程序作业也可能失败,因为它们设置为在过去时间开始。
我们可以做些什么来进一步调查?
我怀疑你没有配置足够的工作线程来处理这些工作。您还应该确保不会长时间阻塞工作线程,因为这可能导致工作线程池耗尽并导致失火。
此外,如果您的cron计划不是限制性的,您可以尝试将失火设置设置为 WithMisfireHandlingInstructionFireAndProceed 以继续触发执行,直到它通过。
答案 2 :(得分:0)
过去我和你一样有同样的问题。我不知道错误的起源。无论如何,我采取了以下措施来解决它:
1-)将作业工作减少到最小化。我使用MSMQ将JobExecution排队,QUARTZ计划只将新消息放入队列(这解决了我的所有问题)
2-)你可以避免作业中的线程同步等事情。
3-)您可以避免的其他修复是QUARTZ版本更新。从2.1.2升级到2.2.1
时,我开始面临这个问题希望这对你有帮助!