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
Spring-statemachine Action不能并发执行的问题的更多相关文章
- spring in action 4th --- quick start
读spring in action. 环境搭建 quick-start依赖注入 面向切面 1.环境搭建 jdk1.8 gradle 2.12 Intelij idea 2016.2.1 1.1创建一个 ...
- Spring in action记录
最近一段时间重新学习了一遍SPRING,现在对这些笔记整理一下,一来算是对之前的学习有一个交代,二来当是重新学习一次,三来可以留下备份 这次学习中以SPRING IN ACTION 4这学习资料,整书 ...
- quartz中设置Job不并发执行
使用quartz框架可以完成定时任务处理即Job,比如有时候我们设置1个Job每隔5分钟执行1次,后来会发现当前Job启动的时候上一个Job还没有运行结束,这显然不是我们期望的,此时可以设置quart ...
- Spring in Action 4th 学习笔记 之 AOP
前提:本文中的AOP仅限于Spring AOP. 先说说为什么需要AOP 最简单的一个例子就是日志记录,如果想记录一些方法的执行情况,最笨的办法就是修改每一个需要记录的方法.但这,真的很笨... 好的 ...
- spring quartz使用多线程并发“陷阱”
定义一个job:ranJob,设置每秒执行一次,设置不允许覆盖并发执行 <bean id="rankJob" class="com.chinacache.www.l ...
- 使用Spring StateMachine框架实现状态机
spring statemachine刚出来不久,但是对于一些企业的大型应用的使用还是十分有借鉴意义的. 最近使用了下这个,感觉还是挺好的. 下面举个例子来说下吧: 创建一个Spring Boot的基 ...
- 【说解】在shell中通过mkfifo创建命名管道来控制多个进程并发执行
背景: 工作中有两个异地机房需要传数据,数据全名很规范,在某个目录下命名为统一的前缀加上编号.如/path/from/file.{1..100}.而机房间的专线对单个scp进程的传输速度是有限制的,比 ...
- 1、Spring In Action 4th笔记(1)
Spring In Action 4th笔记(1) 2016-12-28 1.Spring是一个框架,致力于减轻JEE的开发,它有4个特点: 1.1 基于POJO(Plain Ordinary Jav ...
- SSIS Design3:并发执行
1,利用优先约束来并发处理数据,Data Flow Task 和 Data Flow Task 1 是并发执行的,而 Data Flow Task2 必须等到 Data Flow Task 和 Dat ...
随机推荐
- NEC芯片特别说明
NEC芯片注意点: 1.堆栈和RAM共用,而且是程序中自定义堆栈的空间,堆栈是向下递减堆栈. 2.中断中标志位的清除可以不用手动清除,在中断服务程序中运行任何一条指令后自动清除. 3.中断服务程序有且 ...
- tab栏切换
最简单的tab栏切换 html部分 <ul class="tab"> <li class="item">待支付(1)</li> ...
- layer弹窗
layer.alert(content, options, yes) - 普通信息框 它的弹出似乎显得有些高调,一般用于对用户造成比较强烈的关注,类似系统alert,但却比alert更灵便.它的参数是 ...
- NodeJS学习笔记 (4)网络服务-http(ok)
原文:https://github.com/chyingp/nodejs-learning-guide 自己敲代码: http模块概览 大多数nodejs开发者都是冲着开发web server的目的选 ...
- Nginx 安装 自用
hostnamectl set-hostname nginx systemctl stop firewalld.service systemctl disable firewalld.service ...
- caioj 1157 线性筛选素数
注意这道题开得非常大,有2*1e7 自己可以养成一种习惯,如果数据是很容易的话,可以自己手动输入极限数据来测试自己的程序 #include<cstdio> #include<algo ...
- Git学习总结(7)——Git GUI学习教程
前言 之前一直想一篇这样的东西,因为最初接触时,我也认真看了廖雪峰的教程,但是似乎我觉得讲得有点多,而且还是会给我带来很多多余且重复的操作负担,所以我希望能压缩一下它在我工作中的成本,但是搜索了一下并 ...
- Mysql学习总结(27)——Mysql数据库字符串函数
注:sql的移植性比较强,函数的移植性不强,一般为数据库软件特有,例如mysql有mysql的函数,oracle有oracle的函数. 1.concat连接字符串: 从上图中可以看出,直接使用sele ...
- [MST] Derive Information from Models Using Views
Redundant data or caching data is a constant source of bugs. MST adheres to the philosophy that no d ...
- 几种常见sqlalchemy查询:
#简单查询 print(session.query(User).all()) print(session.query(User.name, User.fullname).all ...