在 Job 构造函数抛出异常后从触发器 ERROR 状态恢复?

Recover from trigger ERROR state after Job constructor threw an exception?

使用 Quartz.net 安排作业时,我偶尔会在实例化作业时收到异常。这反过来会导致 Quartz 将作业的触发器设置为错误状态。发生这种情况时,触发器将停止触发,直到发生一些手动干预(重新启动服务,因为我正在使用内存中的作业调度)。

如何防止设置错误状态,或者至少告诉 Quartz 重试处于错误状态的触发器?

异常的原因是由于需要不稳定的网络调用来获取传递给作业构造函数的配置数据。我正在使用自定义 IJobFactory 来执行此操作。

我看到其他对此没有解决的参考:

How can I prevent the error state from being set, or at the very least, tell Quartz to retry triggers that are in the error state?

遗憾的是,在当前版本中,您无法重试这些触发器。根据 Quartz 的文档,

It should be extremely rare for this method to throw an exception - basically only the case where there is no way at all to instantiate and prepare the Job for execution. When the exception is thrown, the Scheduler will move all triggers associated with the Job into the state, which will require human intervention (e.g. an application restart after fixing whatever configuration problem led to the issue with instantiating the Job).

简而言之,您应该遵循良好的面向对象实践:构造函数不应抛出异常。尝试将配置数据的拉取移动到作业的执行阶段(Execute 方法),在该阶段将正确处理重试。这可能意味着通过允许提取数据的构造函数提供 service/func。

郑重声明,我认为这是 Quartz 的设计缺陷。如果一个工作不能被构建一次,那并不意味着它不能总是被构建。这是暂时性错误,应按此处理。停止所有未来计划的作业违反了最小惊讶原则。

无论如何,我的 hack 解决方案是捕获由于我的作业构建而导致的任何错误,而不是抛出错误或 returning null 到 return 自定义 IJob 而不是简单地记录一个错误。这并不完美,但至少不会阻止将来触发作业。

public IJob NewJob(TriggerFiredBundle bundle, IScheduler scheduler)
{
    try
    {
        var job = this.container.Resolve(bundle.JobDetail.JobType) as IJob;
        return job;
    }
    catch (Exception ex)
    {
        this.logger.Error(ex, "Exception creating job. Giving up and returning a do-nothing logging job.");
        return new LoggingJob(this.logger);
    }
}

当触发器实例化IJobclass发生异常时,触发器将其TRIGGER_STATE更改为ERROR,此状态下的触发器将不再触发。

要重新启用触发器,您需要将其状态更改为等待,然后它才能再次触发。 这是您如何重新启用您的失火触发器的示例。

var trigerKey = new TriggerKey("trigerKey", "trigerGroup");
if (scheduler.GetTriggerState(trigerKey) == TriggerState.Error)
{
    scheduler.ResumeTrigger(trigerKey);
}

要将触发器状态更改为WAITING,作者还建议一种方法是手动更新数据库。

[...] You might need to update database manually, but yeah - if jobs cannot be instantiated it's considered quite bad thing and Quartz will flag them as broken.

我创建了另一个计划在应用程序启动时更新处于错误状态的触发器以恢复它们的作业。

UPDATE QRTZ_TRIGGERS SET [TRIGGER_STATE] = 'WAITING' WHERE [TRIGGER_STATE] = 'ERROR'

更多信息见this github discussion

实际上,从错误状态重置触发器的最佳方法是:

private final SchedulerFactoryBean schedulerFactoryBean;
Scheduler scheduler = schedulerFactoryBean.getScheduler();

TriggerKey triggerKey = TriggerKey.triggerKey(triggerName, triggerGroup);
if (scheduler.getTriggerState(triggerKey).equals(Trigger.TriggerState.ERROR)) {
    scheduler.resetTriggerFromErrorState(triggerKey);
}

注:

您永远不应从第三方库或软件手动修改 table 中的记录。如果有任何功能,所有更改都应通过 API 对该库进行。

JobStoreSupport.resetTriggerFromErrorState