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

Spring State Machine实例恢复推荐方案及多用户工作流场景适配咨询

Spring State Machine 工作流场景选型方案(v2.4.0 适配)

核心结论

你当前多用户多工作流的场景,优先选择「状态机实例池 + 状态回填 + 持久化」的混合方案,既能避免频繁初始化状态机的性能开销,也能解决单实例复用的阻塞、并发安全问题。


方案1:单工作流对应独立状态机实例(你的初始方案)

优缺点

  • 优势:状态完全隔离,无并发冲突,开发逻辑简单,不需要额外处理实例抢占、上下文残留问题
  • 劣势:高并发下频繁创建销毁状态机实例会产生明显的性能开销,尤其当状态机定义(状态/事件/拦截器/动作)复杂时,初始化开销会被放大

适配场景:工作流总数量少、并发量低的场景

代码示例

// 状态机基础配置(可根据业务自定义状态、事件枚举)
@Configuration
@EnableStateMachineFactory
public class WorkflowStateMachineConfig extends StateMachineConfigurerAdapter<String, String> {
    @Override
    public void configure(StateMachineStateConfigurer<String, String> states) throws Exception {
        states.withStates()
                .initial("INIT")
                .state("STEP1")
                .state("STEP2")
                .end("FINISH");
    }
    @Override
    public void configure(StateMachineTransitionConfigurer<String, String> transitions) throws Exception {
        transitions.withExternal()
                .source("INIT").target("STEP1").event("SUBMIT")
                .and().withExternal()
                .source("STEP1").target("STEP2").event("AUDIT_PASS")
                .and().withExternal()
                .source("STEP2").target("FINISH").event("FINISH_EXEC");
    }
}

// 业务处理逻辑
@Service
public class WorkflowService {
    @Autowired
    private StateMachineFactory<String, String> stateMachineFactory;
    @Autowired
    private StateMachinePersister<String, String, String> persister; // 可自定义绑定Redis/DB做持久化存储

    public void processEvent(String workflowId, String event) throws Exception {
        // 每个工作流请求创建独立实例
        StateMachine<String, String> stateMachine = stateMachineFactory.getStateMachine(workflowId);
        // 从持久化存储恢复当前工作流状态
        persister.restore(stateMachine, workflowId);
        // 执行事件驱动状态流转
        stateMachine.sendEvent(event);
        // 持久化更新后的状态机
        persister.persist(stateMachine, workflowId);
        // 销毁实例释放资源
        stateMachine.stop();
    }
}

方案2:单实例复用+状态回填

优缺点

  • 优势:仅一次初始化开销,资源占用极低
  • 劣势:状态机本身非线程安全,必须加锁保证串行执行,高并发下会出现严重的请求阻塞,且容易出现上下文残留导致的状态错乱

适配场景:仅单工作流、并发量极低的场景,完全不匹配你的多用户多工作流需求

代码示例

@Service
public class SingleInstanceWorkflowService {
    @Autowired
    private StateMachine<String, String> singleStateMachine;
    @Autowired
    private StateMachinePersister<String, String, String> persister;
    private final ReentrantLock lock = new ReentrantLock();

    public void processEvent(String workflowId, String event) throws Exception {
        lock.lock(); // 必须加锁,否则并发下状态会直接错乱
        try {
            // 回填当前工作流状态
            persister.restore(singleStateMachine, workflowId);
            singleStateMachine.sendEvent(event);
            persister.persist(singleStateMachine, workflowId);
            // 清空上下文,避免残留到下一个工作流请求
            singleStateMachine.getStateMachineAccessor().doWithAllRegions(accessor -> {
                accessor.resetStateMachine(new DefaultStateMachineContext<>(null, null, null, null));
            });
        } finally {
            lock.unlock();
        }
    }
}

推荐方案:实例池化+状态回填

核心逻辑

通过对象池预初始化一批状态机实例,请求到来时从池中取空闲实例,回填对应工作流的状态后执行事件,执行完成后重置实例上下文并归还到池,既消除了频繁初始化的开销,又保证了并发能力,不存在单实例的阻塞问题。

适配场景:你的多用户多工作流高并发场景

代码示例(依赖commons-pool2实现对象池)

<!-- 引入对象池依赖 -->
<dependency>
    <groupId>org.apache.commons</groupId>
    <artifactId>commons-pool2</artifactId>
    <version>2.11.1</version>
</dependency>
// 状态机实例工厂,用于对象池创建实例
public class StateMachinePooledObjectFactory extends BasePooledObjectFactory<StateMachine<String, String>> {
    @Autowired
    private StateMachineFactory<String, String> stateMachineFactory;

    @Override
    public StateMachine<String, String> create() throws Exception {
        StateMachine<String, String> sm = stateMachineFactory.getStateMachine();
        sm.start();
        return sm;
    }

    @Override
    public PooledObject<StateMachine<String, String>> wrap(StateMachine<String, String> stateMachine) {
        return new DefaultPooledObject<>(stateMachine);
    }

    // 归还实例时自动重置上下文,消除上一个请求的残留数据
    @Override
    public void passivateObject(PooledObject<StateMachine<String, String>> p) throws Exception {
        StateMachine<String, String> sm = p.getObject();
        sm.getStateMachineAccessor().doWithAllRegions(accessor -> {
            accessor.resetStateMachine(new DefaultStateMachineContext<>(null, null, null, null));
        });
    }
}

// 对象池配置
@Configuration
public class StateMachinePoolConfig {
    @Bean
    public GenericObjectPool<StateMachine<String, String>> stateMachinePool(StateMachinePooledObjectFactory factory) {
        GenericObjectPoolConfig<StateMachine<String, String>> config = new GenericObjectPoolConfig<>();
        config.setMaxTotal(16); // 可根据压测结果调整,一般为CPU核心数的2-4倍
        config.setMinIdle(4);
        config.setMaxWaitMillis(3000);
        return new GenericObjectPool<>(factory, config);
    }
}

// 业务处理逻辑
@Service
public class PooledWorkflowService {
    @Autowired
    private GenericObjectPool<StateMachine<String, String>> stateMachinePool;
    @Autowired
    private StateMachinePersister<String, String, String> persister;

    public void processEvent(String workflowId, String event) throws Exception {
        StateMachine<String, String> sm = stateMachinePool.borrowObject();
        try {
            persister.restore(sm, workflowId);
            sm.sendEvent(event);
            persister.persist(sm, workflowId);
        } finally {
            stateMachinePool.returnObject(sm);
        }
    }
}

注意事项

  1. 持久化实现可直接使用SSM 2.4.0自带的JpaStateMachinePersister或RedisStateMachinePersister,不需要手动实现序列化逻辑
  2. 实例池大小不需要设置过大,状态机本身执行逻辑很快,16核CPU设置32个实例完全可以支撑每秒数千次的工作流步骤处理请求

内容的提问来源于stack exchange,提问作者Daniel Vilas-Boas

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.10.13 13:15:01