Quartz Java 恢复作业会执行多次
Posted
技术标签:
【中文标题】Quartz Java 恢复作业会执行多次【英文标题】:Quartz Java resuming a job executes it many times 【发布时间】:2010-12-28 08:49:15 【问题描述】:对于我的应用程序,我创建作业并使用 CronTriggers 安排它们。每个作业只有一个触发器,并且作业名称和触发器名称都相同。没有作业共享触发器。
现在,当我创建像这样的 "0/1 * * * * ?" 指示作业每秒执行一次的 cron 触发器时,它工作得很好。
当我第一次通过调用暂停工作时,问题就出现了:
scheduler.pauseJob(jobName, jobGroup);
然后在 50 秒后恢复工作:
scheduler.resumeJob(jobName, jobGroup);
我看到的是,在这 50 秒内,作业没有按要求执行。但是在我恢复工作的那一刻,我看到同时执行了 50 次工作!!!
我认为这是由于失火指令的默认设置,但即使在创建时将触发器的失火指令设置为这样:
trigger.setMisfireInstruction(CronTrigger.MISFIRE_INSTRUCTION_DO_NOTHING);
同样的事情也会发生。任何人都可以提出解决此问题的方法吗?
【问题讨论】:
【参考方案1】: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;
【讨论】:
非常感谢 :) 这就像一个魅力。暂停工作这样简单的任务会产生这样的问题,这似乎很奇怪。【参考方案2】:如果您暂停作业,触发器将继续触发,但执行将排队等待作业恢复。这不是一个误触发的触发器,因此该设置将无效。
我认为,您想做的是以编程方式禁用或删除 cron 触发器,而不是暂停作业。当你想恢复时,再重新添加触发器。
【讨论】:
pauseJob 方法的 javadoc 显示“使用给定名称暂停 JobDetail - 通过暂停其所有当前触发器。”所以我猜触发器正在暂停。触发器本身也没有暂停方法。或者任何类似的东西。只是从作业中删除触发器并重新插入它是我暂停作业的唯一选择吗?我的意思是要让你的调度程序做一件非常微不足道的事情。为什么它不能简单地工作? 嗯,好点子。我发现使用 Quartz 时,尝试各种方法直到某些东西奏效为止是最有成效的方法,因为它并不总是按照锡上所说的那样做。 这非常令人沮丧,因为也没有简单的方法可以简单地从作业中删除触发器。我可以从事无触发的工作吗?如果是,如何?有任何想法吗?我开始对此失去耐心,我已经尝试了几个小时:)【参考方案3】:至少从 1.6.5 开始(触手可及的最早的石英版本),调度程序有一个 pauseTrigger 方法,该方法将名称/组作为参数。这意味着您不必拥有您使用的每种触发器类型的子类,也不必做时髦的删除/插入技巧。
这两个对我来说都很重要,因为 1) 我们的数据库有严格的禁止删除政策,2) 我使用的自定义数据存储不支持触发器子类。
【讨论】:
【参考方案4】:您可以在 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);
使用这些代码,当暂停的作业被恢复时,它不会立即被触发。另一方面,它将在下一个触发时间被触发,该时间由恢复时间计算
【讨论】:
以上是关于Quartz Java 恢复作业会执行多次的主要内容,如果未能解决你的问题,请参考以下文章
如何使用 Quartz 安排作业在一天内多次但固定的时间运行