【问题标题】:Quartz Java resuming a job executes it many timesQuartz Java 恢复作业会执行多次
【发布时间】: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);

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

【问题讨论】:

    标签: java scheduling scheduled-tasks quartz-scheduler


    【解决方案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() &lt; System.currentTimeMillis()) {
      trigger.setNextFireTime(trigger.getFireTimeAfter(null));
      }
      JobDetail job = retrieveJob(conn, status.getJobKey());
      storeTrigger(conn, trigger, job, true, status.getStatus(), false, false);

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

      【讨论】:

        猜你喜欢
        • 2018-11-30
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多