You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

使用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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.10.06 22:48:02