ElasticJob如何确保在同一个Job实例中多个线程不会处理相同的数据 ElasticJob如何确保数据不会被多个Job实例处理 为了解决上述这种情况,ElasticJob引入任务错过补偿执行(misfire ShardingNode.getMisfireNode(each)); 10 } 11 } 其实现方式为分配给该实例下的所有分片创建持久节点{namespace}/jobname/shading/{item}/misfire shardingContexts.getShardingItemParameters().keySet()); 5 execute(shardingContexts, JobExecutionEvent.ExecutionSource.MISFIRE ); 6} 在任务执行完成后检查是否存在{name-space}/jobname/sharding/{item}/misfire节点,如果存在,则首先清除misfie相关的文件,然后执行任务。 答案:在33s这次任务执行完成后,如果后面的任务执行在10s内执行完毕的话,只会触发一次,不会补偿3次,因为Ela-sticJob记录任务错失执行,只是创建了misfire节点,并不会记录错失的次数。
public static final int MISFIRE_INSTRUCTION_IGNORE_MISFIRE_POLICY = -1; MISFIRE_INSTRUCTION_SMART_POLICY MISFIRE_INSTRUCTION_IGNORE_MISFIRE_POLICY 表明对于过期的定时任务将不执行任何过期策略。 org.quartz.Calendar cal) { int instr = getMisfireInstruction(); if(instr == Trigger.MISFIRE_INSTRUCTION_IGNORE_MISFIRE_POLICY = MISFIRE_INSTRUCTION_FIRE_ONCE_NOW; } if (instr == MISFIRE_INSTRUCTION_DO_NOTHING) 而且过期策略不是MISFIRE_INSTRUCTION_IGNORE_MISFIRE_POLICY 。 有了这三个过期指标,过期的定时任务就好筛选了。
SimpleTrigger Misfire策略 SimpleTrigger有几个misfire相关的策略,告诉quartz当misfire发生的时候应该如何处理。 这些策略包括: SimpleTrigger的Misfire策略常量: MISFIRE_INSTRUCTION_SMART_POLICY MISFIRE_INSTRUCTION_IGNORE_MISFIRE_POLICY MisFire策略。 MISFIRE_INSTRUCTION_FIRE_NOW:忽略已经MisFire的触发,并且立即触发一次。这通常只适用于只执行一次的任务。 如果使用smart policy,SimpleTrigger会根据实例的配置及状态,在所有MISFIRE策略中动态选择一种Misfire策略。
MISFIRE_INSTRUCTION_FIRE_NOW MISFIRE_INSTRUCTION_RESCHEDULE_NOW_WITH_EXISTING_REPEAT_COUNT MISFIRE_INSTRUCTION_RESCHEDULE_NOW_WITH_REMAINING_REPEAT_COUNT ---- MISFIRE_INSTRUCTION_FIRE_NOW 忽略已经MisFire的任务,并且立即执行调度。这通常只适用于只执行一次的任务。 类似MISFIRE_INSTRUCTION_RESCHEDULE_NOW_WITH_EXISTING_REPEAT_COUNT,区别在于会忽略已经MisFire的任务 MISFIRE_INSTRUCTION_RESCHEDULE_NEXT_WITH_EXISTING_COUNT ,区别在于会忽略已经MisFire的任务。 MISFIRE_INSTRUCTION_SMART_POLICY 所有的Trigger的MisFire默认值都是这个,大致意思是“把处理逻辑交给聪明的Quartz去决定”。
SimpleTrigger.REPEAT_INDEFINITELY, 10 * 1000); SimpleTrigger Misfire 这些指令以常量形式定义在SimpleTrigger本身,这些指令如下: Misfire Instruction Constants of SimpleTrigger MISFIRE_INSTRUCTION_FIRE_NOW MISFIRE_INSTRUCTION_RESCHEDULE_NOW_WITH_EXISTING_REPEAT_COUNT MISFIRE_INSTRUCTION_RESCHEDULE_NOW_WITH_REMAINING_REPEAT_COUNT MISFIRE_INSTRUCTION_RESCHEDULE_NEXT_WITH_REMAINING_COUNT MISFIRE_INSTRUCTION_RESCHEDULE_NEXT_WITH_EXISTING_COUNT 回顾前面的课程你可以知道,每个触发器都有一个Trigger.MISFIRE_INSTRUCTION_SMART_POLICY指令可用,并且,这个指令对于每个类型的触发器都是缺省的。
Resource API ElasticJob-Lite Distributed Features High Availability Elastic scale in/out Failover Misfire Boot Starter ElasticJob-Cloud Transient Job High Availability Elastic scale in/out Failover Misfire Idempotency Daemon Job High Availability Elastic scale in/out Failover Misfire Idempotency
priority; /** * 日历名称 */ private String calendarName; /** * 失火指令(参数0,1,2) * MISFIRE_INSTRUCTION_IGNORE_MISFIRE_POLICY = -1 * MISFIRE_INSTRUCTION_SMART_POLICY = 0 (默认) * MISFIRE_INSTRUCTION_FIRE_ONCE_NOW = 1 * MISFIRE_INSTRUCTION_DO_NOTHING = 2 */ private Integer misfireInstruction; /** * 任务代理类 = null){ if(cronTriggerBean.getMisfireInstruction() == Trigger.MISFIRE_INSTRUCTION_IGNORE_MISFIRE_POLICY = null){ if(simpleTriggerBean.getMisfireInstruction() == Trigger.MISFIRE_INSTRUCTION_IGNORE_MISFIRE_POLICY
Misfire Instructions(错过触发策略) Trigger还有一个重要的属性misfire(这里应该称为“错过触发策略”更合理,本质就是一次处理触发器错失触发的策略);如果Scheduler 关闭了,或者Quartz线程池中没有可用的线程来执行jJb,此时持久性的Trigger就会错过(miss)其触发时间,即错过触发(misfire)。 当Scheduler启动的时候,查询所有错过触发(misfire)的持久化的Trigger。然后根据它们各自的misfire机制更新Trigger的信息。 当你在项目中使用Quartz时,你应该对各种类型的Trigger的misfire机制都比较熟悉,这些misfire机制在JavaDoc中有说明。 关于misfire机制的细节,会在讲到具体的Trigger时再作介绍。
Elastic-Job 中错过作业重新触发的概念,配置与原理 错过作业重新触发执行功能 文 | 宋小生 7.5 错过重触发功能 7.5.1 错过执行作业概念 错过作业重触发是什么意思呢: 要弄清楚作业的misfire 在系统关闭到重新启动之间的一段时间里,可能有些任务会被 misfire。 Trigger 被暂停(suspend)的一段时间里,有些任务可能会被 misfire。 线程池中所有线程都被占用,导致任务无法被触发执行,造成 misfire。 有状态任务在下次触发时间到达时,上次执行还没有结束。 当开启错过作业重新触发功能后在12:00执行之后为13:00错过的执行补偿一次执行,执行示例图如下: 图 7.5.3 错过执行重新触发作业 7.5.2 错过执行作业配置 在Quartz内部具有个属性为作业的misfire 具体什么节点满足补偿执行呢: 作业开启了错过作业重触发配置, 当前分片项存在sharding/%s/misfire节点 - END -
在系统关闭到重新启动之间的一段时间里,可能有些任务会被 misfire; Trigger 被暂停(suspend)的一段时间里,有些任务可能会被 misfire; 线程池中所有线程都被占用,导致任务无法被触发执行 ,造成 misfire; 有状态任务在下次触发时间到达时,上次执行还没有结束;为了处理 misfired job,Quartz 中为 trigger 定义了处理策略,主要有下面两种: MISFIRE_INSTRUCTION_FIRE_ONCE_NOW :针对 misfired job 马上执行一次; MISFIRE_INSTRUCTION_DO_NOTHING:忽略 misfired job,等待下次触发;默认是MISFIRE_INSTRUCTION_SMART_POLICY 策略(默认是MISFIRE_INSTRUCTION_SMART_POLICY,该策略在CronTrigger中=MISFIRE_INSTRUCTION_FIRE_ONCE_NOW),根据策略更新nextFireTime =MISFIRE_INSTRUCTION_IGNORE_MISFIRE_POLICY.
(Misfire策略的介绍可以参考第四章:关于Trigger的更多细节)。这些策略以常量的形式在CronTrigger接口中定义(常量上包括描述其行为的JavaDoc)。 这些常量包括: MISFIRE_INSTRUCTION_IGNORE_MISFIRE_POLICY MISFIRE_INSTRUCTION_DO_NOTHING MISFIRE_INSTRUCTION_FIRE_NOW 所有触发器都可以使用Trigger.MISFIRE_INSTRUCTION_SMART_POLICY错过触发策略,它就是所有触发器默认的错失触发策略。 “SMART POLICY”策略在使用了CronTrigger的情况下解释为MISFIRE_INSTRUCTION_FIRE_NOW。 在构建CronTriggers时,你可以将misfire指令指定为cron schedule(cron 调度)的一部分(通过CronSchedulerBuilder): trigger = newTrigger
当你在项目中使用Quartz时,你应该对各种类型的trigger的misfire机制都比较熟悉,这些misfire机制在JavaDoc中有说明。 SimpleTrigger Misfire策略 SimpleTrigger有几个misfire相关的策略,告诉quartz当misfire发生的时候应该如何处理。 这些策略包括: SimpleTrigger的Misfire策略常量: MISFIRE_INSTRUCTION_IGNORE_MISFIRE_POLICY MISFIRE_INSTRUCTION_FIRE_NOW 如果使用smart policy,SimpleTrigger会根据实例的配置及状态,在所有MISFIRE策略中动态选择一种Misfire策略。 说明包括: CronTrigger的Misfire指令常数 MISFIRE_INSTRUCTION_IGNORE_MISFIRE_POLICY MISFIRE_INSTRUCTION_DO_NOTHING
misfire策略 misfire用于Trigger触发时,线程池中没有可用的线程或者调度器被关闭了,此时这个Trigger就变成了misfire。 当下次调度器启动或者有线程可用时,会检查处于misfire状态的Trigger。而misfire的状态值决定了调度器如何处理这个Trigger。 如果9点misfire了,在10:15系统恢复之后。9点,10点的misfire会马上执行。 假设9点,10点的任务都misfire了,系统在10:15恢复后,只会执行一次misfire,下次正点执行。 了,系统在10:15恢复后,只会执行一次misfire, # 下次正点执行。
, seconds=interval, id=id, kwargs=job_func_params, executor='default', next_run_time=next_run_time, misfire_grace_time , seconds=interval, id=id, kwargs=job_func_params, executor='default', next_run_time=next_run_time, misfire_grace_time =retval)) logger.info('Job "%s" executed successfully', job) return events 这里面有个参数是misfire_grace_time ,默认是1s,如果任务的实际执行时间与任务调度时间的时间差>misfire_grace_time,就会warning并且跳过这次任务的调度!!! 1)executor并发度不够,你添加的任务太多 2) misfire_grace_time,还是太小了 2)如果你使用的trigger=interval,并且设置了misfire_grace_time
这个任务在同一分钟内被连续触发了两次,由于本身订单的生成逻辑就不是幂等的,所以导致系统最终生成了重复的数据三、原因分析:定时任务的三种执行策略后面排查后我发现,问题主要是出来底层定时任务Quartz的Misfire 三种执行策略:策略类型行为描述立即执行所有错过的任务(misfire)会立即执行执行一次只执行一次 misfire 任务,合并部分错过的执行放弃执行所有 misfire 任务都跳过,等待下一个周期在我这个定时任务的配置中 五、总结和建议 需要去理解 Misfire 策略的本质不同的策略适合不同场景,根据所需的业务要求来搭配相适应的策略。
Elastic-job 通过补偿执行(misfire)和幂等(monitorExecution)解决了两个问题: 保证同一个job的多个实例不会处理到相同的数据 确保数据不会被多个job实例处理 misfire jobFacade.misfireIfRunning(shardingContexts.getShardingItemParameters().keySet())) { // 如果当前分片被标记为了misfire 如果存在,则调用setMisfire,给当前实例的所有分片创建namespace/jobname/shading/{item}/misfire节点: public void setMisfire(final shardingContexts.getShardingItemParameters().keySet()); execute(shardingContexts, JobExecutionEvent.ExecutionSource.MISFIRE 在任务执行完成后检查是否存在namespace/jobname/sharding/{item}/misfire节点,如果存在,则首先清除misfie相关的文件,然后执行任务(同一任务不管错过多少次,都只会补偿执行一次
START_TIME BIGINT(13) NOT NULL, END_TIME BIGINT(13) NULL, CALENDAR_NAME VARCHAR(200) NULL, MISFIRE_INSTR IDX_QRTZ_T_NFT_ST ON QRTZ_TRIGGERS(SCHED_NAME,TRIGGER_STATE,NEXT_FIRE_TIME); CREATE INDEX IDX_QRTZ_T_NFT_MISFIRE ON QRTZ_TRIGGERS(SCHED_NAME,MISFIRE_INSTR,NEXT_FIRE_TIME); CREATE INDEX IDX_QRTZ_T_NFT_ST_MISFIRE ON QRTZ_TRIGGERS(SCHED_NAME,MISFIRE_INSTR,NEXT_FIRE_TIME,TRIGGER_STATE); CREATE INDEX IDX_QRTZ_T_NFT_ST_MISFIRE_GRP ON QRTZ_TRIGGERS(SCHED_NAME,MISFIRE_INSTR,NEXT_FIRE_TIME,TRIGGER_GROUP,TRIGGER_STATE); CREATE INDEX
NOT NULL, START_TIME BIGINT(13) NOT NULL, END_TIME BIGINT(13) NULL, CALENDAR_NAME VARCHAR(200) NULL, MISFIRE_INSTR IDX_QRTZ_T_NFT_ST ON QRTZ_TRIGGERS(SCHED_NAME,TRIGGER_STATE,NEXT_FIRE_TIME); CREATE INDEX IDX_QRTZ_T_NFT_MISFIRE ON QRTZ_TRIGGERS(SCHED_NAME,MISFIRE_INSTR,NEXT_FIRE_TIME); CREATE INDEX IDX_QRTZ_T_NFT_ST_MISFIRE ON QRTZ_TRIGGERS(SCHED_NAME,MISFIRE_INSTR,NEXT_FIRE_TIME,TRIGGER_STATE); CREATE INDEX IDX_QRTZ_T_NFT_ST_MISFIRE_GRP ON QRTZ_TRIGGERS(SCHED_NAME,MISFIRE_INSTR,NEXT_FIRE_TIME,TRIGGER_GROUP,TRIGGER_STATE); CREATE INDEX
TaskException { switch (job.getMisfirePolicy()) { case ScheduleConstants.MISFIRE_DEFAULT : return cb; case ScheduleConstants.MISFIRE_IGNORE_MISFIRES: return cb.withMisfireHandlingInstructionIgnoreMisfires(); case ScheduleConstants.MISFIRE_FIRE_AND_PROCEED return cb.withMisfireHandlingInstructionFireAndProceed(); case ScheduleConstants.MISFIRE_DO_NOTHING cb.withMisfireHandlingInstructionDoNothing(); default: throw new TaskException("The task misfire
MISFIRE_INSTRUCTION_IGNORE_MISFIRE_POLICY MISFIRE_INSTRUCTION_FIRE_NOW MISFIRE_INSTRUCTION_RESCHEDULE_NOW_WITH_EXISTING_REPEAT_COUNT MISFIRE_INSTRUCTION_RESCHEDULE_NOW_WITH_REMAINING_REPEAT_COUNT MISFIRE_INSTRUCTION_RESCHEDULE_NEXT_WITH_REMAINING_COUNT MISFIRE_INSTRUCTION_RESCHEDULE_NEXT_WITH_EXISTING_COUNT 其中 Trigger.MISFIRE_INSTRUCTION_SMART_POLICY