Quartz.NET - 计划任务停止随机运行

时间:2014-09-26 01:57:44

标签: c# quartz.net quartz.net-2.0

我们为不同的时间表运行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)我们可以做些什么来进一步调查?

3 个答案:

答案 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

时,我开始面临这个问题

希望这对你有帮助!