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

Spring State Machine触发外部事件时频繁报Retry Exhausted错误如何解决

问题根因

报错的核心原因是并发向状态机发送事件,Spring Statemachine 3.x底层基于Reactor实现,事件发送要求严格串行,你当前的两个事件发送逻辑存在并发冲突:

  • 定时任务TimerAction内部调用sendEvent发送RETRY事件,timer触发的任务运行在parallel调度器线程池
  • 业务代码主动调用sendEvent发送ERROR事件,运行在业务线程
    两个线程同时发射事件触发Reactor Sink的串行校验失败,抛出Sinks$EmissionException,内部重试10次全部失败后抛出重试耗尽错误。
解决方案
  • 1. 移除Action中主动发事件的冗余逻辑(最优先)

你当前的timer转移完全不需要自定义Action发事件,直接将timer转移的目标指向TESTING即可,从根源避免并发冲突,修改转移配置如下:

transitions
    // 替换原来INITIALIZING的internal timer转移
    .withExternal()
        .source(DBStates.INITIALIZING)
        .target(DBStates.TESTING)
        .timer(100).and()
    .withJunction()
        .source(DBStates.TESTING)
        .first(DBStates.DOWN, isDown())
        .then(DBStates.READY, isReady())
        .last(DBStates.BAD_PASSWORD).and()
    .withExternal()
        .source(DBStates.BAD_PASSWORD)
        .target(DBStates.TESTING)
        .event(DBStateChanges.PASSWORD_RECEIVED).and()
    // 替换原来DOWN状态的internal timer转移
    .withExternal()
        .source(DBStates.DOWN)
        .target(DBStates.TESTING)
        .timer(10000).and()
    .withExternal()
        .source(DBStates.DOWN)
        .target(DBStates.TESTING)
        .event(DBStateChanges.RETRY).and()
    .withExternal()
        .source(DBStates.INITIALIZING)
        .target(DBStates.TESTING)
        .event(DBStateChanges.RETRY).and()
    .withExternal()
        .source(DBStates.READY)
        .target(DBStates.TESTING)
        .event(DBStateChanges.ERROR);

修改完成后可直接删除冗余的TimerAction定义和对应的Bean配置。

  • 2. 优化外部事件发送逻辑

调用Reactive API发送事件时添加错误处理,避免未处理的异常抛出:

public void triggerStateChange() {
    Message<DBStateChanges> m = new GenericMessage<>(DBStateChanges.ERROR); 
    statemachine.sendEvent(Mono.just(m))
        // 自定义异常处理
        .doOnError(e -> log.error("状态机事件发送失败", e))
        // 异常场景降级,避免错误向上传播
        .onErrorResume(e -> Mono.empty())
        .subscribe();
}
  • 3. 升级依赖版本

你当前使用的Spring State Machine 3.0.1是3.x分支的早期版本,存在多个Reactor并发处理相关的已知bug,建议升级到3.0.x分支的最新维护版本,可直接修复部分底层并发问题。

  • 4. 临时兼容方案

如果确实有场景需要在Action中发送事件,可配置状态机使用同步执行器,强制所有事件处理串行化:

@Override
public void configure(StateMachineConfigurationConfigurer<DBStates, DBStateChanges> config) throws Exception {
    config.withConfiguration()
        // 同步执行器,所有事件处理都在调用线程执行,避免并发
        .taskExecutor(new SyncTaskExecutor());
}

该方案会牺牲状态机的并发处理性能,仅推荐临时验证问题使用。

内容的提问来源于stack exchange,提问作者cstack

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.09.27 19:15:03