zoukankan      html  css  js  c++  java
  • Spring-statemachine Action不能并发执行的问题

    Spring-statemachine版本:当前最新的1.2.3.RELEASE版本

    这几天一直被Action是串行执行搞得很郁闷,写了一个demo专门用来测试:

    public static void main(String[] args) throws Throwable {
            StateMachineBuilder.Builder<String, String> builder = StateMachineBuilder.builder();
    
            StaticListableBeanFactory beanFactory = new StaticListableBeanFactory();
    
            ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();
            executor.setCorePoolSize(30);
            executor.setKeepAliveSeconds(30000);
            executor.setMaxPoolSize(30);
            executor.setQueueCapacity(3000);
            executor.initialize();
    
            builder.configureConfiguration()
                .withConfiguration()
                    .beanFactory(beanFactory)
                    .taskExecutor(executor)
                    .taskScheduler(new ConcurrentTaskScheduler())
                    .listener(new StateMachineListenerAdapter<String, String>() {
                        @Override
                        public void stateEntered(State<String, String> state) {
                            String id = state == null ? null : state.getId();
                            System.out.println("entered " + id);
                        }
    
                        @Override
                        public void stateExited(State<String, String> state) {
                            String id = state == null ? null : state.getId();
                            System.out.println("exited " + id);
                        }
                    });
    
            StateMachineStateConfigurer<String, String> smsConfigurer = builder.configureStates();
            smsConfigurer.withStates()
                .initial("READY")
                .fork("FORK")
                .state("TASKS")
                .join("JOIN")
                .choice("CHOICE")
                .state("ERROR")
                .and()
                .withStates()
                .parent("TASKS")
                .initial("T1")
                .end("T1E")
                .and()
                .withStates()
                .parent("TASKS")
                .initial("T2")
                .end("T2E");
    
    
            StateMachineTransitionConfigurer<String, String> smtConfigurer = builder.configureTransitions();
            smtConfigurer.withExternal()
                .source("READY").target("FORK")
                .and()
                .withFork()
                .source("FORK").target("TASKS")
                .and()
                .withJoin()
                .source("TASKS").target("JOIN")
                .and()
                .withExternal()
                .source("T1").target("T1E")
                .action(getAction())
                .and()
                .withExternal()
                .source("T2").target("T2E")
                .action(getAction())
                .and()
                .withExternal()
                .source("JOIN").target("CHOICE")
                .and()
                .withChoice()
                .source("CHOICE")
                .first("ERROR", c -> true)
                .last("READY");
    
            StateMachine<String, String> stateMachine = builder.build();
    
            stateMachine.start();
        }
    
        public static Action<String, String> getAction() {
            return c -> {
                System.out.println(">>>>>>>>>>>>>>>>>>>>>>>>>>..");
                try {
                    Thread.sleep(5000);
                } catch (InterruptedException e) {
                    ;
                }
                System.out.println("<<<<<<<<<<<<<<<<<<<<<<<<<<..");
            };
        }
    }
    

    打出来的日志是

    entered READY
    exited READY
    entered TASKS
    entered T1
    >>>>>>>>>>>>>>>>>>>>>>>>>>..
    entered T2
    <<<<<<<<<<<<<<<<<<<<<<<<<<..
    exited T1
    entered T1E
    >>>>>>>>>>>>>>>>>>>>>>>>>>..
    <<<<<<<<<<<<<<<<<<<<<<<<<<..
    exited T2
    entered T2E
    exited TASKS
    entered ERROR
    

    如果是并发执行的话,应该像是这样:

    >>>>>>>>>>>>>>>>>>>>>>>>>>..
    >>>>>>>>>>>>>>>>>>>>>>>>>>..
    <<<<<<<<<<<<<<<<<<<<<<<<<<..
    <<<<<<<<<<<<<<<<<<<<<<<<<<..
    

    然后趁着周末debug一发,跟着代码一步步走,发现org.springframework.statemachine.config.AbstractStateMachineFactory类中有这段代码:

    // 从所有region的transition和当前region的state中抽取出当前region的transition
    //
    // in代表所有的transition(包括父状态机),stateDatas代表当前region(子状态机)的状态
    private Collection<TransitionData<S, E>> resolveTransitionData(Collection<TransitionData<S, E>> in, Collection<StateData<S, E>> stateDatas) {
        ArrayList<TransitionData<S, E>> out = new ArrayList<TransitionData<S,E>>();
    
        Collection<Object> states = new ArrayList<Object>();
        for (StateData<S, E> stateData : stateDatas) {
            states.add(stateData.getParent()); // 抽出父状态机的状态
        }
    
        for (TransitionData<S, E> transitionData : in) {
            S state = transitionData.getState(); // 从下一行代码可以推理得出此处的getState得到的是父状态机的状态
            if (state != null && states.contains(state)) { // 核心代码,如果父状态机的状态包含state,就加入到out去当作子状态机的transition,最后返回子状态机的transition集合
                out.add(transitionData);
            }
        }
    
        return out;
    }
    

    看到了吧,我当时猜测构建transition时还有一个state方法用来标识父状态机的状态,于是到withExternal()后面尝试看看有没有一个叫做state()的方法,结果还真有!
    然后代码改成这样:

    smtConfigurer
        .withExternal()
            .source("READY").target("FORK")
            .and()
        .withFork()
            .source("FORK").target("TASKS")
            .and()
        .withJoin()
            .source("TASKS").target("JOIN")
            .and()
        .withExternal()
            .state("TASKS") // 增加本行设置父状态机的状态
            .source("T1").target("T1E")
            .action(getAction())
            .and()
        .withExternal()
            .state("TASKS") // 增加本行设置父状态机的状态
            .source("T2").target("T2E")
            .action(getAction())
            .and()
        .withExternal()
            .source("JOIN").target("CHOICE")
            .and()
        .withChoice()
            .source("CHOICE")
            .first("ERROR", c -> true)
            .last("READY");
    

    结果日志终于并发执行了,好开心啊~

    entered READY
    exited READY
    entered TASKS
    entered T2
    entered T1
    >>>>>>>>>>>>>>>>>>>>>>>>>>..
    >>>>>>>>>>>>>>>>>>>>>>>>>>..
    <<<<<<<<<<<<<<<<<<<<<<<<<<..
    <<<<<<<<<<<<<<<<<<<<<<<<<<..
    exited T2
    exited T1
    entered T2E
    entered T1E
    exited TASKS
    entered SUCCESS
    

    关于设置transition的state,官方的reference并没有相关的说明,所以初次使用spring-statemachine做并发任务状态管理的话,基本上都会遇到这个问题。

    Github issue: https://github.com/spring-projects/spring-statemachine/issues/336

  • 相关阅读:
    sql 有条件计数
    easyui combox 手动添加项
    只有设置了 name 属性的表单元素才能在提交表单时传递它们的值
    除湿方法
    android 页面跳转,数据回传
    android studio 乱码
    android studio 工具
    gridlaylout 简单布局
    android 开发 简单的页面布局
    android sdk
  • 原文地址:https://www.cnblogs.com/lanhj/p/6623126.html
Copyright © 2011-2022 走看看