25

对于我的应用程序,我创建作业并使用 CronTriggers 安排它们。每个作业只有一个触发器,并且作业名称和触发器名称都相同。没有作业共享触发器。

现在当我创建一个像这样“0/1 * * * *?”的cron触发器时 它指示作业每秒执行一次,它工作得很好。

当我第一次通过调用暂停工作时,问题就出现了:

scheduler.pauseJob(jobName, jobGroup);

然后在假设 50 秒后恢复工作:

scheduler.resumeJob(jobName, jobGroup);

我看到的是,在这 50 秒内,作业没有按要求执行。但是在我恢复工作的那一刻,我看到同时执行了 50 次工作!!!

我认为这是由于失火指令的默认设置,但即使在创建时将触发器的失火指令设置为此:

trigger.setMisfireInstruction(CronTrigger.MISFIRE_INSTRUCTION_DO_NOTHING);

同样的事情也会发生。任何人都可以提出解决此问题的方法吗?

4

4 回答 4

34

CronTrigger通过记住的作品nextFireTime。创建触发器后nextFireTime初始化。每次触发作业时nextFireTime都会更新。由于暂停时未触发作业nextFireTime保持“旧”。因此,在您恢复工作后,触发器将返回每个旧触发器时间。

问题是,触发器不知道它正在暂停。为了克服这个问题,有这种失火处理。恢复作业后,updateAfterMisfire()将调用触发器的方法来纠正nextFireTime. 但如果nextFireTime和 now 之间的差异小于misfireThreshold则不会。然后永远不会调用该方法。此阈值的默认值为 60,000。因此,如果您的暂停时间超过 60 秒,一切都会好起来的。

既然你有问题,我认为不是。;) 要解决此问题,您可以修改阈值或使用简单的包装器CronTrigger

public class PauseAwareCronTrigger extends CronTrigger {
    // constructors you need go here

    @Override
    public Date getNextFireTime() {
        Date nextFireTime = super.getNextFireTime();
        if (nextFireTime.getTime() < System.currentTimeMillis()) {
            // next fire time after now
            nextFireTime = super.getFireTimeAfter(null);
            super.setNextFireTime(nextFireTime);
        }
        return nextFireTime;
    }
}
于 2009-12-27T19:02:36.467 回答
6

如果您暂停作业,触发器将继续触发,但执行将排队等待作业恢复。这不是一个误触发的触发器,因此该设置将无效。

我认为您想要做的是以编程方式禁用或删除 cron 触发器,而不是暂停工作。当你想恢复时,再重新添加触发器。

于 2009-12-19T18:05:43.993 回答
1

至少从 1.6.5 开始(触手可及的最早版本的石英),调度程序有一个 pauseTrigger 方法,该方法将名称/组作为参数。这意味着您不必拥有您使用的每种触发器类型的子类,也不必做时髦的删除/插入技巧。

这两个对我来说都很重要,因为 1) 我们的数据库有严格的禁止删除政策,以及 2) 我使用的自定义数据存储不支持触发器子类。

于 2010-02-14T05:36:09.677 回答
0

您可以在 org.quartz.impl.jdbcjobstore.JobStoreSupport#resumeTrigger(Connection conn, TriggerKey key) 中添加这些代码

OperableTrigger trigger = getDelegate().selectTrigger(conn, key);
if (trigger.getNextFireTime().getTime() < System.currentTimeMillis()) {
trigger.setNextFireTime(trigger.getFireTimeAfter(null));
}
JobDetail job = retrieveJob(conn, status.getJobKey());
storeTrigger(conn, trigger, job, true, status.getStatus(), false, false);

使用这些代码,当暂停的作业被恢复时,它不会立即被触发。另一方面,它将在下一个触发时间被触发,该时间由恢复时间计算

于 2021-04-12T08:28:31.680 回答