如何为Axon的Quartz Deadline Manager配置失火指令及处理逾期截止日期?
解决Axon Quartz Deadline失火(Misfire)问题
1. 修正Quartz初始化配置,避免触发器数据丢失
你的spring.quartz.jdbc.initialize-schema=always配置会在每次应用启动时重建Quartz数据库表,直接导致之前调度的触发器被清空,这是重启后触发器消失的核心原因。将其修改为:
spring.quartz.jdbc.initialize-schema=never
如果是首次部署需要初始化表结构,可临时设置为always,初始化完成后改回never。
2. 显式配置Axon触发器的Misfire指令
Axon的QuartzDeadlineManager不会自动继承Quartz全局的misfire配置,需要通过triggerCustomizer为每个创建的触发器指定失火处理策略。修改你的QuartzDeadlineManager Bean配置:
@Bean public QuartzDeadlineManager quartzDeadlineManager( Scheduler scheduler, Configuration configuration, Serializer serializer, TransactionManager transactionManager ) { ScopeAwareProvider scopeAwareProvider = new ConfigurationScopeAwareProvider(configuration); return QuartzDeadlineManager.builder() .scheduler(scheduler) .serializer(serializer) .scopeAwareProvider(scopeAwareProvider) .transactionManager(transactionManager) // 配置触发器失火后立即执行 .triggerCustomizer(triggerBuilder -> triggerBuilder.withMisfireInstruction(SimpleTrigger.MISFIRE_INSTRUCTION_FIRE_NOW) ) .build(); }
这样创建的触发器会将QRTZ_TRIGGERS表的MISFIRE_INSTR列设置为1,符合你期望的“立即重试”逻辑。
3. 验证Quartz集群配置
确保你的Quartz集群配置正确,避免节点间的触发器处理冲突:
spring.quartz.properties.org.quartz.jobStore.isClustered=true spring.quartz.properties.org.quartz.scheduler.instanceId=AUTO spring.quartz.properties.org.quartz.jobStore.misfireThreshold=1000
misfireThreshold设置为1000ms(1秒),表示超过1秒未触发的任务会被判定为失火,这个配置是合理的。
备选方案:启动时扫描未处理的截止日期
如果上述配置仍无法覆盖所有场景(比如极端情况下触发器数据丢失),可以在应用启动时主动扫描未处理的截止日期:
- 维护一个读模型,记录所有已调度但未触发的截止日期(包含截止时间、聚合ID、命令信息)
- 在应用启动类中添加初始化逻辑,查询读模型中截止时间早于当前时间且未标记为已处理的记录
- 对这些记录直接下发对应的命令,或者重新调度截止日期
示例初始化逻辑:
@Component public class MissedDeadlineProcessor implements ApplicationListener<ContextRefreshedEvent> { private final CommandGateway commandGateway; private final DeadlineReadModelRepository deadlineReadModelRepository; public MissedDeadlineProcessor(CommandGateway commandGateway, DeadlineReadModelRepository deadlineReadModelRepository) { this.commandGateway = commandGateway; this.deadlineReadModelRepository = deadlineReadModelRepository; } @Override public void onApplicationEvent(ContextRefreshedEvent event) { LocalDateTime now = LocalDateTime.now(); // 查询所有已过期且未处理的截止日期 List<DeadlineReadModel> missedDeadlines = deadlineReadModelRepository.findByDeadlineTimeBeforeAndProcessedFalse(now); for (DeadlineReadModel model : missedDeadlines) { // 下发对应的命令 commandGateway.send(new TriggerMissedDeadlineCommand(model.getAggregateId(), model.getDeadlineId())); // 标记为已处理 model.setProcessed(true); deadlineReadModelRepository.save(model); } } }
内容的提问来源于stack exchange,提问作者Double M
相关产品推荐
相关产品推荐

