quartz 之misfire

misfire 到底怎么理解?

 
misfire 的概念并不是源自quartz ,理解它是很重要的,否则,很多奇怪的现象会让你莫名其妙。
 
misfire 简单来说,就是因为某些原因(比如服务器挂了,崩溃了,手动停止了,太繁忙了), 导致某些应该被调度的任务没有被调度, 就会可能造成misfire —— 注意,这里仅仅是说可能, 意味着,任务没有被调度也并不一定造成misfire ,因为misfire 还有一个条件,只要满足了俩条件,才算是misfire :
 
1、 不管原因,任务应该被调度,但实际没有被调度
2、 没有被调度的时间,超过了指定时间。
 
这里的指定时间, 就是通过 org.quartz.jobStore.misfireThreshold: 60000 设置
 
 

如何处理misfire ?

 
首先,misfire 肯定是不受欢迎的,出现了misfire , 大家都不高兴; 但是没办法, misfire 的事件会时常发生的。 我们也不能对其放任不管吧。
quartz提供了多个解决方案,
 
其实很复杂, 因为需要处理各种 策略
 
org.quartz.Trigger#MISFIRE_INSTRUCTION_SMART_POLICY
org.quartz.Trigger#MISFIRE_INSTRUCTION_IGNORE_MISFIRE_POLICY
 
org.quartz.CronTrigger#MISFIRE_INSTRUCTION_FIRE_ONCE_NOW
org.quartz.CronTrigger#MISFIRE_INSTRUCTION_DO_NOTHING
 
org.quartz.DailyTimeIntervalTrigger#REPEAT_INDEFINITELY
org.quartz.DailyTimeIntervalTrigger#MISFIRE_INSTRUCTION_FIRE_ONCE_NOW
org.quartz.DailyTimeIntervalTrigger#MISFIRE_INSTRUCTION_DO_NOTHING
 
org.quartz.SimpleTrigger#MISFIRE_INSTRUCTION_FIRE_NOW
...
 
每个不同类型的Trigger 会有不同的处理策略。 这么多策略呢,让人发晕, 但是其实也没那么难理解, 只要理解了一两个,后面的就容易了。
 
基本上这么几类策略:
1 不管直接misfire 了多少次,只执行一次, 1.1 立即执行, 1.2
2 不管直接misfire 了多少次,全部忽略
3 misfire 了多少次,就立即补充执行多少次, 不考虑总数
4 misfire 了多少次,就立即补充执行多少次, 但是不能超过总数
...
 
0 就是 SMART_POLICY, 基本也就是只处理一次。
 
 

调度的 准确度问题

版本是 2.2.1 ,我发现了调度的 准确度问题!( 实际不是bug)
 
这个奇怪的 准确度问题是我之前在不太懂misfire 及misfire策略的时候遇到的,开始还以为是bug ,非常不理解,后面发现, 我x, 原来这么回事!
 
首先设置 每秒钟执行一次,
SimpleScheduleBuilder builder = SimpleScheduleBuilder
// 设置执行次数
.repeatSecondlyForTotalCount(50);
 
测试发现 6s 竟然
第一次(新建)执行了11次, 第二次(重复执行) 6s 执行了 25次,
 
简直无法理解。。
 
发现是第一秒 执行了很多次, 后面是正常的,
 
第三次, 则基本恢复了正常, 6s 执行了7次, 应该是6s , 控制不准确。
 
难道是misfire ?
 
发现 ,20:22:19.043 [QuartzScheduler_dufy_test-NON_CLUSTERED_MisfireHandler] DEBUG o.q.impl.jdbcjobstore.JobStoreTX - Found 0 triggers that missed their scheduled fire-time.
 
 
那 难道, 没有misfire 也会导致 累积?
 
 
 
++++
 
6s 执行了 5、 20、 17 、25 次!
CronScheduleBuilder builder = CronScheduleBuilder.cronSchedule("0/2 * * * * ?");
 
 
发现确实是 misfire 的问题, 因为重新执行的时间小于 misfire的间隔,所以被当做了 错过的触发,导致了 积累
 
 
如果超过了 misfire间隔, 反而呢, 被当做 misfire处理,
single 、cron默认是MISFIRE_INSTRUCTION_SMART_POLICY, 它会立即执行 全部的错过的触发 :
Instructs the Scheduler that upon a mis-fire situation, the updateAfterMisfire() method will be called on the Trigger to determine the mis-fire instruction, which logic will be trigger-implementation-dependent.
In order to see if this instruction fits your needs, you should look at the documentation for the getSmartMisfirePolicy() method on the particular Trigger implementation you are using.
 
所以了,不是quartz 的bug,虽然,确实让人感觉到奇怪,有些不好理解,但它是准确的!!我们 需要特别注意设置 org.quartz.jobStore.misfireThreshold 参数。
 
 
 

查询 是否有misfire 的job

2021-07-25 13:25:06 | Time 8 ms | statement-1 | SELECT TRIGGER_NAME, TRIGGER_GROUP FROM qrtz_TRIGGERS WHERE SCHED_NAME = 'dufy_test' AND NOT (MISFIRE_INSTR = -1) AND NEXT_FIRE_TIME < 1627190646874 AND TRIGGER_STATE = 'WAITING' ORDER BY NEXT_FIRE_TIME ASC, PRIORITY DESC
13:25:06.905 [main] DEBUG o.q.impl.jdbcjobstore.JobStoreTX - Found 0 triggers that missed their scheduled fire-time.
 
 

misfire的实现机制

它是什么时候启动的 org.quartz.impl.jdbcjobstore.JobStoreSupport#schedulerStarted
 
/**
* @see org.quartz.spi.JobStore#schedulerStarted()
*/
public void schedulerStarted() throws SchedulerException {
 
if (isClustered()) {
  clusterManagementThread = new ClusterManager();
  if(initializersLoader != null)
    clusterManagementThread.setContextClassLoader(initializersLoader);
  clusterManagementThread.initialize();
} else {
  try {
    recoverJobs();
  } catch (SchedulerException se) {
    throw new SchedulerConfigException(
    "Failure occured during job recovery.", se);
  }
}
 
  misfireHandler = new MisfireHandler();
  if(initializersLoader != null)
    misfireHandler.setContextClassLoader(initializersLoader);
    misfireHandler.initialize();
    schedulerRunning = true;
 
  getLog().debug("JobStore background threads started (as scheduler was started).");

}
 


版权声明
本文原创发表于 博客园,作者为 阿K .     本文欢迎转载,但未经作者同意必须保留此段声明,且在文章页面明显位置给出原文连接,否则视为侵权。
欢迎关注本人微信公众号:觉醒的码农,或者扫码进群:

原文地址:https://www.cnblogs.com/FlyAway2013/p/15172153.html