【问题标题】:How do I force a quartz.net job to restart intervall after completion如何强制quartz.net 作业在完成后重新启动间隔
【发布时间】:2014-09-19 09:13:04
【问题描述】:

我有一个项目,我使用TopShelfTopShelf.Quartz

关注this example 我正在与

                s.ScheduleQuartzJob(q =>
                    q.WithJob(() => JobBuilder.Create<MyJob>().Build())
                    .AddTrigger(() => TriggerBuilder.Create()
                        .WithSimpleSchedule(builder => builder
                            .WithIntervalInSeconds(5)
                            .RepeatForever())
                        .Build())
                );

即使前一个仍在运行,它也会每五秒触发一次我的工作。我真正想要实现的是开始一项工作,完成后等待五秒钟,然后重新开始。这是可能的还是我必须实现自己的逻辑(例如通过静态变量)。

【问题讨论】:

    标签: c# .net quartz.net topshelf


    【解决方案1】:

    @NateKerkhofs 建议的作业监听器将起作用,如下所示:

    public class RepeatAfterCompletionJobListener : IJobListener
    {
        private readonly TimeSpan interval;
    
        public RepeatAfterCompletionJobListener(TimeSpan interval)
        {
            this.interval = interval;
        }
    
        public void JobExecutionVetoed(IJobExecutionContext context)
        {
        }
    
        public void JobToBeExecuted(IJobExecutionContext context)
        {
        }
    
        public void JobWasExecuted(IJobExecutionContext context, JobExecutionException jobException)
        {
           string triggerKey = context.JobDetail.Key.Name + ".trigger";
    
            var trigger = TriggerBuilder.Create()
                    .WithIdentity(triggerKey)
                    .StartAt(new DateTimeOffset(DateTime.UtcNow.Add(interval)))
                    .Build();
    
            context.Scheduler.RescheduleJob(new TriggerKey(triggerKey), trigger);
        }
    
        public string Name
        {
            get
            {
                return "RepeatAfterCompletionJobListener";
            }
        }
    }
    

    然后将监听器添加到调度器中:

    var jobKey = "myJobKey";
    var schedule = new StdSchedulerFactory().GetScheduler();
    listener = new
       RepeatAfterCompletionJobListener(TimeSpan.FromSeconds(5));
    
    schedule.ListenerManager.AddJobListener
             (listener, KeyMatcher<JobKey>.KeyEquals(new JobKey(jobKey)));
    
    var job = JobBuilder.Create(MyJob)
                    .WithIdentity(jobKey)
                    .Build();
    
    // Schedule the job to start in 5 seconds to give the service time to initialise
    var trigger = TriggerBuilder.Create()
                    .WithIdentity(CreateTriggerKey(jobKey))
                    .StartAt(DateTimeOffset.Now.AddSeconds(5))
                    .Build();
    
    schedule.ScheduleJob(job, trigger);
    

    不幸的是,我不知道如何使用 Typshelf.Quartz 库使用的流畅语法来做到这一点(或者是否可以做到),我将它与 TopShelf 和常规 Quartz.Net 一起使用。

    【讨论】:

    • 您实际上创建了一个作业侦听器而不是触发器侦听器,因为您继承自 IJobListener。我提交了修改。
    【解决方案2】:

    您可以使用 TriggerListener (http://www.quartz-scheduler.net/documentation/quartz-2.x/tutorial/trigger-and-job-listeners.html) 来监听触发器何时完成,然后在 5 秒内重新安排。

    另一个选项是将下一个作业安排为作业本身的执行中的最终操作。

    http://www.quartz-scheduler.net/documentation/faq.html 在 2/3 的地方有一个问题可以解释更多。

    【讨论】:

      【解决方案3】:

      JobListener 解决方案是一种非常强大且灵活的方式,可以在完成后重新安排您的工作。感谢 Nate Kerkhofs 和 stuartd 的投入。

      在我的情况下,使用 DisallowConcurrentExecution 属性来装饰我的 Job 类就足够了,因为我的工作没有不同的实例

      [DisallowConcurrentExecution]
      public class MyJob : IJob
      {
      }
      

      仅供参考:使用带有TopShelf.Quartz 的 JobListerener 代码可能如下所示

      var jobName = "MyJob";
      var jobKey = new JobKey(jobName);
      
      s.ScheduleQuartzJob(q =>
                 q.WithJob(() => JobBuilder.Create<MyJob>()
                      .WithIdentity(jobKey).Build())
                  .AddTrigger(() => TriggerBuilder.Create()
                      .WithSimpleSchedule(builder => builder
                          .WithIntervalInSeconds(5)
                      .Build())
      
      var listener = new RepeatAfterCompletionJobListener(TimeSpan.FromSeconds(5));
      var listenerManager = ScheduleJobServiceConfiguratorExtensions
            .SchedulerFactory().ListenerManager;
      listenerManager.AddJobListener(listener, KeyMatcher<JobKey>.KeyEquals(jobKey));
      

      如果您正在使用TopShelf.Quartz.Ninject(和我一样),请不要忘记在拨打ScheduleJobServiceConfiguratorExtensions.SchedulerFactory()之前先拨打UseQuartzNinject()

      【讨论】:

      • 我本打算将 DisallowConcurrentExecution 作为一种可能的解决方案,但您的问题是“完成后等待五秒钟,然后重新开始”。
      • 是的,我的解释有点误导。我的工作可以运行零秒到一分钟之间的任何内容(取决于要处理的数据量)。使用DisallowConcurrentExecution,我的作业每五秒执行一次,或者在执行后立即执行超过五秒。在了解该属性之前,我认为最好的解决方案是告诉 Quartz 在工作完成后而不是在解雇工作后开始 inverval。对我来说,属性方法就足够了,但我很确定 JobListener 方法会派上用场,因为我计划向我的应用程序添加更多工作。
      • 另请注意,您需要从具体类型 JobBuilder.Create&lt;MyJob&gt; 而不是 JobBuilder.Create&lt;IMyJob&gt; 创建作业,以便获取该属性。
      【解决方案4】:

      我发现最好的方法是添加简单的 Job Listener。 在我的示例中,它会在失败后重新安排工作。 当然,您可以在 .StartAt(DateTime.UtcNow) 中添加延迟

      public class QuartzRetryJobListner : IJobListener
      {
          public string Name => GetType().Name;
          public async Task JobExecutionVetoed(IJobExecutionContext context, CancellationToken cancellationToken = default) => await Task.CompletedTask;
          public async Task JobToBeExecuted(IJobExecutionContext context, CancellationToken cancellationToken = default) => await Task.CompletedTask;
      
      
          public async Task JobWasExecuted(
              IJobExecutionContext context,
              JobExecutionException jobException,
              CancellationToken cancellationToken = default)
          {
              if (jobException == null) return;
      
              // Create and schedule new trigger
              ITrigger retryTrigger = TriggerBuilder.Create()
                   .StartAt(DateTime.UtcNow)
                   .Build();
      
              await context.Scheduler.ScheduleJob(context.JobDetail, new[] { retryTrigger }, true);
          }
      }
      

      另外,我认为添加类扩展很有用

      public static class QuartzExtensions
      {
          public static void RepeatJobAfterFall(this IScheduler scheduler, IJobDetail job)
          {
              scheduler.ListenerManager.AddJobListener(
                  new QuartzRetryJobListner(),
                  KeyMatcher<JobKey>.KeyEquals(job.Key));
          }
      }
      

      只是为了简化使用。

      _scheduler.ScheduleJob(job, trigger);
      //In case of failue repeat job immediately
      _scheduler.RepeatJobAfterFall(job);
      

      【讨论】:

        猜你喜欢
        • 2010-11-29
        • 1970-01-01
        • 2016-07-05
        • 1970-01-01
        • 1970-01-01
        • 2013-10-02
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多