Fork and Join: проблема с параллельным выполнением двух регионов в конечной машине spring

Я попытался использовать структуру "fork/join" для параллельного выполнения конечного автомата в регионах состояния "S2". Конфигурация конечного автомата была основана на модели uml. А у меня проблема с параллельной работой этих двух регионов. Сначала конечный автомат проходит состояния от S20 до S23, а затем проходит состояния от S30 до S33.

Где может быть проблема? Спасибо заранее за вашу помощь.

Это моя конфигурация конечного автомата:

@Slf4j
@Configuration
@EnableStateMachine
public class StateMachineConfig extends StateMachineConfigurerAdapter<String, String> {

    @Override
    public void configure(StateMachineModelConfigurer<String, String> model) throws Exception {
        model
                .withModel()
                .factory(modelFactory());
    }

    @Bean
    public StateMachineModelFactory<String, String> modelFactory() {
        Resource model = new ClassPathResource("/uml/simple-forkjoin.uml");
        return new UmlStateMachineModelFactory(model);
    }


     @Override public void configure(StateMachineConfigurationConfigurer<String, String> config) throws Exception {
     config
     .withConfiguration()
     .listener(new StateMachineListener())
     .autoStartup(true);
     }
}

Пример класса, представляющего состояние:

@Slf4j
@WithStateMachine
public class S22 {


    @OnTransition(target = "S22")
    public void onTransition(StateContext<String, String> stateContext) {
        log.info(Colours.ANSI_BLUE + "target = \"S22\", getState().getId(): "
                + stateContext.getStateMachine().getState().getId()
                + Colours.ANSI_RESET);
        Sleep.sleep(1000);
    }

    @OnStateChanged(target = "S22")
    public void onStateChanged(StateContext<String, String> stateContext) {
        log.debug(Colours.B_HI_ANSI_GREEN + "S22 EXPECTS_ORDERS stateChanged :: Current state ID :: {}",
                stateContext.getStateMachine().getState().getId() + Colours.ANSI_RESET);
        Sleep.sleep(1000);
    }
}

журналы:

2019-03-21 12:25:01.746  INFO 7648 --- [nio-8080-exec-2] o.s.web.servlet.DispatcherServlet        : Completed initialization in 8 ms
2019-03-21 12:25:01.780  INFO 7648 --- [nio-8080-exec-2] c.f.f.config.StateMachineListener        : transition started! target state: S0
2019-03-21 12:25:01.788  INFO 7648 --- [nio-8080-exec-2] com.fj.forkJointest.forkJoin.S0          : target = "S0, getState().getId(): SI
2019-03-21 12:25:01.791 DEBUG 7648 --- [nio-8080-exec-2] com.fj.forkJointest.forkJoin.S0          : S0 EXPECTS_ORDERS stateChanged :: Current state ID :: S0
2019-03-21 12:25:01.791  INFO 7648 --- [nio-8080-exec-2] c.f.f.config.StateMachineListener        : state changed from: SI to S0
2019-03-21 12:25:01.791  INFO 7648 --- [nio-8080-exec-2] c.f.f.config.StateMachineListener        : transition started! target state: S1
2019-03-21 12:25:01.792  INFO 7648 --- [nio-8080-exec-2] com.fj.forkJointest.forkJoin.S1          : target = "S1", getState().getId(): S0
2019-03-21 12:25:01.794  INFO 7648 --- [nio-8080-exec-2] c.f.f.config.StateMachineListener        : transition started! target state: S21
2019-03-21 12:25:01.794  INFO 7648 --- [nio-8080-exec-2] com.fj.forkJointest.forkJoin.S21         : target = "S21", getState().getId(): S2
2019-03-21 12:25:02.795  INFO 7648 --- [nio-8080-exec-2] c.f.f.config.StateMachineListener        : transition started! target state: S22
2019-03-21 12:25:02.797  INFO 7648 --- [nio-8080-exec-2] com.fj.forkJointest.forkJoin.S22         : target = "S22", getState().getId(): S2
2019-03-21 12:25:03.799  INFO 7648 --- [nio-8080-exec-2] c.f.f.config.StateMachineListener        : transition started! target state: S23
2019-03-21 12:25:03.801  INFO 7648 --- [nio-8080-exec-2] com.fj.forkJointest.forkJoin.S23         : target = "S23", getState().getId(): S2
2019-03-21 12:25:04.803  INFO 7648 --- [nio-8080-exec-2] c.f.f.config.StateMachineListener        : state changed from: S22 to S23
2019-03-21 12:25:04.803  INFO 7648 --- [nio-8080-exec-2] o.s.s.support.LifecycleObjectSupport     : started org.springframework.statemachine.support.DefaultStateMachineExecutor@76b2b847
2019-03-21 12:25:04.803  INFO 7648 --- [nio-8080-exec-2] c.f.f.config.StateMachineListener        : State machine started :: S2
2019-03-21 12:25:04.804  INFO 7648 --- [nio-8080-exec-2] o.s.s.support.LifecycleObjectSupport     : started S23 S22 S20 S21  / S23 / uuid=51391415-d1f5-440f-9c8e-677c00bcdd50 / id=null
2019-03-21 12:25:04.805 DEBUG 7648 --- [nio-8080-exec-2] com.fj.forkJointest.forkJoin.S22         : S22 EXPECTS_ORDERS stateChanged :: Current state ID :: S2
2019-03-21 12:25:05.806  INFO 7648 --- [nio-8080-exec-2] c.f.f.config.StateMachineListener        : state changed from: S21 to S22
2019-03-21 12:25:05.806 DEBUG 7648 --- [nio-8080-exec-2] com.fj.forkJointest.forkJoin.S21         : S21 EXPECTS_ORDERS stateChanged :: Current state ID :: S2
2019-03-21 12:25:06.807  INFO 7648 --- [nio-8080-exec-2] c.f.f.config.StateMachineListener        : state changed from: S20 to S21
2019-03-21 12:25:06.808 DEBUG 7648 --- [nio-8080-exec-2] com.fj.forkJointest.forkJoin.S20         : S20 EXPECTS_ORDERS stateChanged :: Current state ID :: S2
2019-03-21 12:25:07.809  INFO 7648 --- [nio-8080-exec-2] c.f.f.config.StateMachineListener        : state changed from: null to S20
2019-03-21 12:25:07.809  INFO 7648 --- [nio-8080-exec-2] c.f.f.config.StateMachineListener        : transition started! target state: S31
2019-03-21 12:25:07.810  INFO 7648 --- [nio-8080-exec-2] com.fj.forkJointest.forkJoin.S31         : target = "S31", getState().getId(): S2
2019-03-21 12:25:08.211  INFO 7648 --- [nio-8080-exec-2] c.f.f.config.StateMachineListener        : transition started! target state: S32
2019-03-21 12:25:08.212  INFO 7648 --- [nio-8080-exec-2] com.fj.forkJointest.forkJoin.S32         : target = "S32", getState().getId(): S2
2019-03-21 12:25:08.613  INFO 7648 --- [nio-8080-exec-2] c.f.f.config.StateMachineListener        : transition started! target state: S33
2019-03-21 12:25:08.614  INFO 7648 --- [nio-8080-exec-2] com.fj.forkJointest.forkJoin.S33         : target = "S33", getState().getId(): S2
2019-03-21 12:25:09.015  INFO 7648 --- [nio-8080-exec-2] c.f.f.config.StateMachineListener        : transition started! target state: S3
2019-03-21 12:25:09.016  INFO 7648 --- [nio-8080-exec-2] com.fj.forkJointest.forkJoin.S3          : target = "S3", getState().getId(): S2
2019-03-21 12:25:09.016  INFO 7648 --- [nio-8080-exec-2] c.f.f.config.StateMachineListener        : transition started! target state: S3
2019-03-21 12:25:09.017  INFO 7648 --- [nio-8080-exec-2] com.fj.forkJointest.forkJoin.S3          : target = "S3", getState().getId(): S2
2019-03-21 12:25:09.018  INFO 7648 --- [nio-8080-exec-2] o.s.s.support.LifecycleObjectSupport     : stopped org.springframework.statemachine.support.DefaultStateMachineExecutor@76b2b847
2019-03-21 12:25:09.018  INFO 7648 --- [nio-8080-exec-2] o.s.s.support.LifecycleObjectSupport     : stopped S23 S22 S20 S21  /  / uuid=51391415-d1f5-440f-9c8e-677c00bcdd50 / id=null
2019-03-21 12:25:09.019 DEBUG 7648 --- [nio-8080-exec-2] com.fj.forkJointest.forkJoin.S4          : S4 EXPECTS_ORDERS stateChanged :: Current state ID :: S4
2019-03-21 12:25:09.020  INFO 7648 --- [nio-8080-exec-2] c.f.f.config.StateMachineListener        : state changed from: S2 to S4

Это мой проект на github


person Brutus    schedule 21.03.2019    source источник


Ответы (2)


Spring State Machine использует TaskExecutor для выполнения региона и по умолчанию синхронно. Чтобы добиться асинхронного выполнения, вам необходимо переопределить исполняющую задачу по умолчанию.

Вы можете сделать это несколькими способами:

Используя StateMachineConfigurationConfigurer< /а>:

@Override
public void configure(StateMachineConfigurationConfigurer<States, Events> config) throws Exception {
    config
        .withConfiguration()
            //other configs
            .taskExecutor(myAsyncTaskExecutor())
}

public TaskExecutor myAsyncTaskExecutor() {
    ThreadPoolTaskExecutor taskExecutor = new ThreadPoolTaskExecutor();
    taskExecutor.setCorePoolSize(5);
    return taskExecutor;
}

Или просто объявить bean-компонент, который будет автоматически забираться из SM:

@Bean(name = StateMachineSystemConstants.TASK_EXECUTOR_BEAN_NAME)
public TaskExecutor taskExecutor() {
    ThreadPoolTaskExecutor taskExecutor = new ThreadPoolTaskExecutor();
    taskExecutor.setCorePoolSize(5);
    return taskExecutor;
}
person hovanessyan    schedule 22.03.2019
comment
Спасибо, Ованесян, Ваш метод сработал, заявив Бина. Но только когда я изменил структуру Fork в UML. Вместо двух стрелок, указывающих на две машины (UML не работает), я использовал одну (UML работает ), указывая на состояние S2, в котором они есть. В противном случае все выполняется в том же потоке (eTaskExecutor-3), за исключением состояния SI, инициирующего главный конечный автомат (eTaskExecutor-1). Ваш первый вариант вызвал ошибку: ... Ошибка создания bean-компонента с именем «stateMachine»: ... ThreadPoolTaskExecutor не инициализирован - person Brutus; 25.03.2019
comment
В чем может быть проблема? - person Brutus; 25.03.2019
comment
@Brutus просто следуйте официальному примеру fork/join и примите его в соответствии с вашим случаем docs.spring.io/autorepo/docs/spring-statemachine/1.2.x-SNAPSHOT/ Также, если ответ решил вашу проблему, рассмотрите возможность его принятия чтобы другие могли видеть, что это решает проблему, описанную в вопросе - person hovanessyan; 25.03.2019
comment
@Brutus Я пытался изменить UML, как вы упомянули, но он у меня не работает. - person Ankur Singhal; 15.07.2020

Исполнитель должен быть определен, как показано ниже.

public TaskExecutor myAsyncTaskExecutor() {
            ThreadPoolTaskExecutor taskExecutor = new ThreadPoolTaskExecutor();
            taskExecutor.setCorePoolSize(5);
            taskExecutor.initialize();
            return taskExecutor;
        }

Конфигурация конечного автомата

.taskExecutor(myAsyncTaskExecutor()).taskScheduler(new ConcurrentTaskScheduler())
person Ankur Singhal    schedule 15.07.2020