一. 简介
1. 什么是哑火
由于某些原因导致触发器(trigger)在该触发的时候没有得到触发,后续对应的解决策略即为哑火策略。(个人理解)
2. 哑火触发的条件
①:所有的工作线程都在忙碌,导致某些trigger得不到触发.(如:simplethreadpool 默认是10个工作线程,但我有15个trigger同时触发, 恰巧这10个trigger关联的job耗时都很长,剩下的5个trigger超过了等待时间仍然没有得到触发)
②:调度器(sheduler)中途挂了,某个时间又恢复了
③:设置的trigger的开始时间早于当前时间
如果没有触发哑火的条件,则不会触发哑火策略,之前的全部都是按照忽略来计算的,后续按照正常规律来进行。如果不设置开始时间,即为当前时间开始,不触发哑火策略。
下面介绍两类Trigger对应的哑火策略:SimpleTrigger和CronTrigger (前提:案例设置时间早于当前时间的,即都是有错过的)
二. SimpleTrigger哑火策略详解
这里要分三种情况来讨论,执行指定次数的情况WithRepeatCount(n),n=1、n>1、n=forever三种情况,重点理解n>1的情况,另外两种都是他的特殊情况而已。
(一):执行指定次数的情况(WithRepeatCount(n) n>1) 只要记住这种情况,下面的B和C都能分析出来
(1).默认 :立即执行,修改当前调度时间,总数保持不变 (等价于WithMisfireHandlingInstructionNowWithExistingCount)
详见下面解析
(2).WithMisfireHandlingInstructionIgnoreMisfires:错过的立即追赶,然后正常调度。
PS:设置的时间早于当前时间,执行的时候,会将当前时间之前错过的次数一次性执行完,然后正常按照设置的开始时间及规律进行执行。 如果设置的RepeatCount(n)中n的次数小于错过的次数,只能执行n次,执行完后,将不会在执行了,因为执行次数已经用完了。
案例:设置的开始时间为8:00,每隔半小时执行一次,执行总次数为5次,当前时间为9:05,那么开始时候会先执行3次,将错过的一次性执行了,然后按照正常调度执行,下一次执行的时间为9:30,还能执行两次。
(3).WithMisfireHandlingInstructionNextWithExistingCount:错过的不管了,按计划等待下次调度,总数不变,结束时间推迟。
PS:错过的次数不处理,仍按照设置的规律来执行,执行次数不变,要执行完的RepeatCount(n)中的n.
案例:设置的开始时间为8:00,每隔半小时执行一次,执行总次数为5次,当前时间为9:05,那么开始时候并没有调度执行,第一次执行调度的时间为9:30,然后按照正常调度执行,总共执行5次,最后一次时间为11:30。
(4).WithMisfireHandlingInstructionNextWithRemainingCount:错过的不管了,按计划等待下次调度,但总数要减去misfire错过的次数
PS:错过的次数不处理,仍按照设置的规律来执行,执行总数要 减去 错过的次数!
案例:设置的开始时间为8:00,每隔半小时执行一次,执行总次数为5次,当前时间为9:05,那么开始时候并没有调度执行,第一次执行调度的时间为9:30,然后按照正常调度执行,总共执行 5-3=2 次,最后一次时间为10:00。
(5).WithMisfireHandlingInstructionNowWithExistingCount: 立即执行,修改当前调度时间,总数保持不变
PS:立即执行,修改当前调度时间的含义为,即使我设置的开始时间早于当前时间,但该哑火策略会立马执行该触发器,即运行后,马上执行了一次,后续的时间间隔均是是以当前执行时间为基础来进行的,言外之意,之前设置的开始时间没用了,执行总数不变。
案例:设置的开始时间为8:00,每隔半小时执行一次,执行总次数为5次,当前时间为9:05,那么开始时候立即执行,第一次执行调度的时间为9:05,然后以9:05为基础,按照正常调度规律执行,总共执行5次,第二次时间为9:35,最后一次执行时间为11:05。
(6).WithMisfireHandlingInstructionNowWithRemainingCount: 立即执行,修改当前调度时间,总数要减去misfire错过的次数
PS:立即执行,修改当前调度时间的含义为,即使我设置的开始时间早于当前时间,但该哑火策略会立马执行该触发器,即运行后,马上执行了一次,后续的时间间隔均是是以当前执行时间为基础来进行的,言外之意,之前设置的开始时间没用了。执行总数要 减去 错过的次数!
案例:设置的开始时间为8:00,每隔半小时执行一次,执行总次数为5次,当前时间为9:05,那么开始时候立即执行,第一次执行调度的时间为9:05,然后以9:05为基础,按照正常调度规律执行,总共执行 5-3=2 次,第二次(即最后一次)时间为9:35。
B:只执行一次的情况 (WithRepeatCount(n) n=1) 对A情况的一个特殊分析
(1). 默认:(等价于WithMisfireHandlingInstructionFireNow)
(2). WithMisfireHandlingInstructionFireNow:立即执行
PS:设置的时间早于当前时间,执行的时候,立即把这一次执行完,后续将不再执行;但设置的时间晚于当前时间,则按照正常规律进行执行了
(3).WithMisfireHandlingInstructionNextWithRemainingCount 和 WithMisfireHandlingInstructionNowWithRemainingCount :不执行了
PS:设置的时间早于当前时间,执行的时候按照原规律执行,但是次数要减去错过的次数,这里总共就执行一次,所以就不执行了
C:永久执行的情况(RepeatForever) 对A情况的一个特殊分析
(1). 默认:等价与下面的(2),按计划的正常调度执行,执行次数永久执行
(2). WithMisfireHandlingInstructionNextWithExistingCount 和 WithMisfireHandlingInstructionNextWithRemainingCount:按计划的正常调度执行,执行次数永久执行
(3). WithMisfireHandlingInstructionNowWithExistingCount 和 WithMisfireHandlingInstructionNowWithRemainingCount: 立即执行,执行的开始时间改为当前时间,执行次数永久执行
代码分享:自行替换即可
public static void misfireShow()
{
//1.创建Schedule
IScheduler scheduler = StdSchedulerFactory.GetDefaultScheduler();
//2.创建job (具体的job需要单独在一个文件中执行)
var job = JobBuilder.Create<HelloJob4>().Build();
//3.配置trigger
//下面的四个触发器对应了上面四种情况的测试,时间需要根据实际情况进行配置测试来设置
var trigger1 = TriggerBuilder.Create().StartAt(DateBuilder.DateOf(, , ))
.WithSimpleSchedule(x => x.WithIntervalInMinutes()
.WithRepeatCount()
.WithMisfireHandlingInstructionNowWithExistingCount()
).Build();
var trigger2 = TriggerBuilder.Create().StartAt(DateBuilder.DateOf(, , ))
.WithSimpleSchedule(x => x.WithIntervalInSeconds()
.WithRepeatCount()
.WithMisfireHandlingInstructionFireNow()
).Build();
var trigger3 = TriggerBuilder.Create()
.StartAt(DateBuilder.DateOf(, , , , , ))
.WithSimpleSchedule(x => x.WithIntervalInMinutes()
.WithMisfireHandlingInstructionNowWithRemainingCount()
.RepeatForever()).Build();
var trigger4 = TriggerBuilder.Create()
.StartAt(DateBuilder.DateOf(, , ))
.WithCronSchedule("0 0-59 9-23 ? * MON-FRI",x=>x.WithMisfireHandlingInstructionIgnoreMisfires()).Build(); //4.开始调度
scheduler.ScheduleJob(job, trigger1);
scheduler.Start();
}
三. CronTrigger哑火策略详解
(1). 默认:错过的合并,于当前时间执行一次,不修改调度时间,按计划等待下一次调度(等价于下面的 WithMisfireHandlingInstructionFireAndProceed)
(2). WithMisfireHandlingInstructionIgnoreMisfires:错过的立即追赶,然后正常调度
ps:错过多少次,初次执行的时候追赶多少次,追赶的次数的时间是按原规律执行的时间,然后按照原规律进行正常后续调度
(3). WithMisfireHandlingInstructionFireAndProceed:错过的合并,于当前时间执行一次,不修改调度时间,按计划等待下一次调度
PS:无论错过多少次,均在初次运行的时候,即当前时间执行一次,后续的执行仍按照原规律进行执行。
(4). WithMisfireHandlingInstructionDoNothing:错过的不管了,后续按照正常调度进行
PS:无论错过多少次,均忽略,后续的执行仍按照原规律进行执行。
代码详见上面的代码分享