最近使用Quartz.Net想实现使用Cron来执行Job,并且若当前作业执行时间越过了下一个执行周期,则跳过这个周期.

我的测试Job使用延迟来模拟执行中的效果,为了方便,Cron使用了 0/5 * * * * ? (每五秒一次)

Job的延迟使用了Delay

await Task.Delay(7000);

Trigger周期设置

.WithCronSchedule("0/5 * * * * ? ")

image.png

出来的效果,每达到一次周期,会再启动一次,无视未执行完的Job,翻阅相关内容后得知需要设置 MisFire策略

然后按照官方MisFire说明调整周期设置

MisfireInstruction.IgnoreMisfirePolicy

MisfireInstruction.CronTrigger.DoNothing

MisfireInstruction.CronTrigger.FireOnceNow

.WithCronSchedule("0/5 * * * * ? ", x => x.WithMisfireHandlingInstructionDoNothing())

这里选择了DoNothing,大致就是错过周期后,这里不做任何事情,就相当于跳过这个周期。

执行下,不起效果,任务依然会在第二个周期直接跑,无视第一周期还没执行完的Job

又翻了半小时资料,发现标签 [DisallowConcurrentExecution] 用于阻止Job并发的情况

在Job上加上标签,再测试下

[DisallowConcurrentExecution]
public class TestJob : IJob

执行下,至少没有了并发的情况,但再第一周期执行完后直接跑第二周期,即使错过了,也会跑一次,导致后面的周期都乱了,这不是我想要的效果。
image.png

接着查了一小时资料,很多文章的方式都不能达到想到的效果。本来想着放弃,试最后一次stackoverflow的答案

答案描述是修改Quartz的MisFire阈值,尝试了下,能达到预想的结果!

https://stackoverflow.com/questions/30358718/getting-quartz-net-to-ignore-misfires

代码如下

var config = new System.Collections.Specialized.NameValueCollection();
config.Add("quartz.jobStore.misfireThreshold", "2000");
StdSchedulerFactory factory = new StdSchedulerFactory(config);

其实Quartz的MisFire策略是存在一个tolerate(容忍)度的,大意在错过某个周期,若周期距离现在还有多长时间,如这个时间不在阈值范围内,则会触发失火状态。

而前面找到的MisFire策略,是在失火状态下才会有效果,官方文档中可以查到,失火阈值,默认是60秒

image.png

而在我自己的测试代码里面,周期与错过的时间是在2~3秒内!

问题就是在这里,所以在我加上WithMisfireHandlingInstructionDoNothing策略依然达不到我的效果,因为时间太短,在这个阈值(容忍)范围内

所以,想达到短周期Job能跳过周期的效果,需要做到几点:

第一,需要在Job加上阻止并发的标签[DisallowConcurrentExecution],这样Job就能错过周期

第二,如上Quartz的配置,需要将misfireThreshold设置得小一点,比如1s或者0.5s

第三,CronSchedule加上WithMisfireHandlingInstructionDoNothing失火下不做处理策略

按照上面这几点,就能达到预想的效果.
image.png

Q.E.D.


随意游世