Quartz Java恢复工作会多次执行它

26

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

现在,当我创建类似于"0/1 * * * * ?" 的cron触发器时,它可以正常工作,指示作业每秒执行一次。

问题出现在我第一次通过调用以下命令暂停作业时:

scheduler.pauseJob(jobName, jobGroup);

然后等待50秒后,使用以下代码恢复作业:

scheduler.resumeJob(jobName, jobGroup);

我看到的是这个任务在50秒内没有按照要求执行。但是当我恢复任务时,我看到任务立即执行了50次!

我原以为这是由于触发器默认的misfire指令设置导致的,但即使在创建触发器时将misfire指令设置为以下内容:

trigger.setMisfireInstruction(CronTrigger.MISFIRE_INSTRUCTION_DO_NOTHING);

发生了同样的事情。有人能建议一种修复此问题的方法吗?

4个回答

35
CronTrigger通过记住nextFireTime来工作。创建触发器后,nextFireTime会被初始化。每次触发作业后,nextFireTime都会更新。由于在暂停时不触发作业,nextFireTime保持“旧”的状态。因此,在恢复作业后,触发器将返回每个旧的触发时间。
问题是,触发器不知道它正在暂停。为了解决这个问题,有这个misfire处理。在恢复作业后,触发器的updateAfterMisfire()方法将被调用,以纠正nextFireTime。但是,如果nextFireTime和当前时间之间的差距小于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;
    }
}

4
非常感谢 :) 这个方法非常有效。似乎暂停一个任务这么简单的操作居然会引起这样的问题,有些奇怪。 - Savvas Dalkitsis

6
如果您暂停作业,触发器将继续触发,但执行将排队等待,直到恢复作业。这不是错误触发器,因此该设置将无效。
我认为您想要做的是以编程方式禁用或删除cron触发器,而不是暂停作业。当您想要恢复时,再重新添加触发器。

1
pauseJob方法的javadoc说“暂停具有给定名称的JobDetail-通过暂停其所有当前触发器。”,所以我猜测触发器正在被暂停。而且触发器本身没有暂停方法。或者任何类似的东西。是将触发器从作业中删除并重新插入它是我唯一的暂停作业选项吗?我的意思是,这是一个非常微不足道的事情,希望您的调度程序能够完成。为什么它不能简单地工作呢? - Savvas Dalkitsis
嗯,说得好。我发现在Quartz中,尝试各种方法直到找到有效的方法是最有效的方法,因为它并不总是按照说明书上所说的那样运行。 - skaffman
这真的很令人沮丧,因为没有简单的方法可以从任务中删除触发器。我能拥有一个没有触发器的作业吗?如果可以,应该怎么做?你有任何想法吗?我已经试了几个小时了,开始失去耐心 :) - Savvas Dalkitsis

1

至少从1.6.5版本开始(我手头上最早的quartz版本),调度器有一个pauseTrigger方法,它接受名称/组作为参数。这意味着您不必为每种触发器类型都创建子类,也不必进行奇怪的删除/插入操作。

对我来说,这两点都很重要:1)我们的数据库有严格的无删除策略;2)我使用的自定义数据存储不支持触发器子类。


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);

使用这些代码,当暂停的作业恢复时,它不会立即触发。相反,它将在下一个计算出的恢复时间触发。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接