最近使用Quartz.Net想实现使用Cron来执行Job,并且若当前作业执行时间越过了下一个执行周期,则跳过这个周期.
我的测试Job使用延迟来模拟执行中的效果,为了方便,Cron使用了 0/5 * * * * ?
(每五秒一次)
Job的延迟使用了Delay
await Task.Delay(7000);
Trigger周期设置
.WithCronSchedule("0/5 * * * * ? ")
出来的效果,每达到一次周期,会再启动一次,无视未执行完的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
执行下,至少没有了并发的情况,但再第一周期执行完后直接跑第二周期,即使错过了,也会跑一次,导致后面的周期都乱了,这不是我想要的效果。
接着查了一小时资料,很多文章的方式都不能达到想到的效果。本来想着放弃,试最后一次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秒
而在我自己的测试代码里面,周期与错过的时间是在2~3秒内!
问题就是在这里,所以在我加上WithMisfireHandlingInstructionDoNothing
策略依然达不到我的效果,因为时间太短,在这个阈值(容忍)范围内
所以,想达到短周期Job能跳过周期的效果,需要做到几点:
第一,需要在Job加上阻止并发的标签[DisallowConcurrentExecution]
,这样Job就能错过周期
第二,如上Quartz的配置,需要将misfireThreshold
设置得小一点,比如1s或者0.5s
第三,CronSchedule加上WithMisfireHandlingInstructionDoNothing
失火下不做处理策略
按照上面这几点,就能达到预想的效果.
Q.E.D.