使用StateMachineRuntimePersister时如何正确搭配使用timerOnce定时器
Spring Statemachine 3.0.1 分布式场景下timerOnce定时器最优解决方案
核心实现思路
基于框架原生能力做3处改造即可解决问题,不需要引入额外的Quartz调度组件:
- 保留原生
timerOnce配置,开启触发器持久化能力 - 给定时触发的转换加持久化状态校验守卫,避免脏状态执行
- 增加分布式锁控制,避免集群多实例重复触发
具体实现步骤
1. 开启触发器持久化配置
在状态机配置类中,确保StateMachineRuntimePersister开启trigger持久化,3.0.1版本可通过配置参数spring.statemachine.persist.runtime.persist-triggers=true开启,所有timer的调度元数据会同步存入JPA对应表中,即使状态机实例被销毁,下次获取实例时会自动恢复未触发的定时器。
2. 给定时转换加状态校验Guard
给S1跳S2的定时转换配置自定义Guard,触发时主动从JPA仓库拉取最新的状态机状态,校验一致才允许执行转换:
// 自定义状态校验Guard @Component public class PersistedStateGuard<S, E> implements Guard<S, E> { @Autowired private JpaStateMachineRepository<S, E> jpaRepository; @Override public boolean evaluate(GuardContext<S, E> context) { String machineId = context.getStateMachine().getId(); // 从数据库拉取最新状态 StateMachineInstance<S, E> persistedInstance = jpaRepository.findById(machineId).orElse(null); if (persistedInstance == null) { return false; } // 校验当前持久化状态是否为S1,和触发要求的状态一致才放行 return Objects.equals(persistedInstance.getState(), States.S1); } }
在转换配置里绑定该Guard:
@Configuration @EnableStateMachine public class StateMachineConfig extends StateMachineConfigurerAdapter<States, Events> { @Autowired private PersistedStateGuard<States, Events> persistedStateGuard; @Override public void configure(StateMachineTransitionConfigurer<States, Events> transitions) throws Exception { transitions .withExternal() .source(States.S1) .target(States.S2) .timerOnce(60000) .guard(persistedStateGuard); // 绑定校验守卫 } }
3. 配置状态机释放和回收策略
- 调用
stateMachineService.releaseStateMachine(machineId, false)不停止实例,保留定时器调度 - 配置
DefaultStateMachineService的闲置实例回收参数,比如设置stateMachineService.setInstanceExpiration(300000),超过5分钟没有被访问的实例会自动停止释放内存,因为定时器元数据已经持久化,下次拉取实例时会自动恢复未触发的timer
4. 增加分布式锁避免重复触发
在Guard里或者状态机的全局监听器中加分布式锁,锁key为sm:timer:lock:{machineId}:s1-to-s2,锁过期时间设置为10s(略大于转换执行的最大耗时),只有拿到锁的实例才允许执行转换逻辑,避免集群多实例同时触发同一个状态机的定时转换。
方案优势
- 完全复用Spring Statemachine原生timer能力,所有状态流转逻辑收敛在状态机配置中,无需额外维护第三方调度任务
- 彻底解决分布式场景下的状态不一致问题,所有触发逻辑都以持久化的最新状态为准
- 自动处理实例宕机、实例回收的场景,定时器不会丢失,也不会重复执行
内容的提问来源于stack exchange,提问作者tangens
相关产品推荐
相关产品推荐

