最近在网上看到了一些关于spring statemachine 的示例,因为自己在学习过程中,看了例子后总是感觉缺少点什么,大家关注的业务参数如何传递说了但是没有给具体示例,下面是我自己在学习中的示例:示例简单实现一个订单状态机,模拟使用三个线程开启三个订单状态的的流转,其中流转过程中传递参数并获取参数;同时自己也遇到几个问题,文末提出;话不多说,上代码:
- 状态及事件定义:
public class Constant {
@AllArgsConstructor
@Getter
public enum OrderStatusEnum {
INIT(0, "初始化"),
RAISING(1, "募集中"),
FULL(2, "满标"),
STAMP_ING(3, "合同签章中"),
STAMP_SUCCESS(4, "合同签署完成"),
LOAN_ING(6, "放款中"),
LOAN_SUCCESS(7, "放款成功"),
;
private int status;
private String desc;
}
@AllArgsConstructor
@Getter
public enum OrderEventEnum {
ORDER_APPLY(0, "进件事件"),
SKU_NOTICE(1, "上架事件"),
SKU_ENOUGH(2, "满标通知事件"),
ORDER_STAMP(3, "合同签章事件"),
STAMP_SUCCESS(4, "签章成功事件"),
LOAN_ING(5, "放款中事件"),
LOAN_SUCCESS(6, "放款成功事件"),
;
private int status;
private String desc;
}
- 状态机定义:
@Configuration
@EnableStateMachine(contextEvents = false)
public class OrderMachine extends EnumStateMachineConfigurerAdapter<OrderStatusEnum, OrderEventEnum> {
@Autowired
private OrderMachineListener orderMachineListener;
@Override
public void configure(StateMachineStateConfigurer<OrderStatusEnum, OrderEventEnum> states) throws Exception {
states.withStates().initial(OrderStatusEnum.INIT).states(EnumSet.allOf(OrderStatusEnum.class));
}
@Override
public void configure(StateMachineTransitionConfigurer<OrderStatusEnum, OrderEventEnum> transitions) throws Exception {
transitions.withExternal()
.source(OrderStatusEnum.INIT).target(OrderStatusEnum.RAISING).event(OrderEventEnum.SKU_NOTICE)
.and()
.withExternal()
.source(OrderStatusEnum.RAISING).target(OrderStatusEnum.FULL).event(OrderEventEnum.SKU_ENOUGH)
.and()
.withExternal()
.source(OrderStatusEnum.FULL).target(OrderStatusEnum.STAMP_ING).event(OrderEventEnum.ORDER_STAMP)
.and()
.withExternal().source(OrderStatusEnum.STAMP_ING).target(OrderStatusEnum.STAMP_SUCCESS).event(OrderEventEnum.STAMP_SUCCESS)
.and()
.withExternal().source(OrderStatusEnum.STAMP_SUCCESS).target(OrderStatusEnum.LOAN_ING).event(OrderEventEnum.LOAN_ING)
.and()
.withExternal().source(OrderStatusEnum.LOAN_ING).target(OrderStatusEnum.LOAN_SUCCESS).event(OrderEventEnum.LOAN_SUCCESS);
}
@Override
public void configure(StateMachineConfigurationConfigurer<OrderStatusEnum, OrderEventEnum> config) throws Exception {
config.withConfiguration().autoStartup(true).listener(orderMachineListener);
}
- 状态机监听器:
@Component
public class OrderMachineListener extends StateMachineListenerAdapter<OrderStatusEnum, OrderEventEnum> {
@Override
public void stateChanged(State<OrderStatusEnum, OrderEventEnum> from, State<OrderStatusEnum, OrderEventEnum> to) {
System.out.println(Thread.currentThread().getName() + " | 状态机监听器:stateChanged | from:" + from + " -> to:" + to);
}
@Override
public void stateContext(StateContext<OrderStatusEnum, OrderEventEnum> stateContext) {
if (stateContext.getStage().equals(StateContext.Stage.STATE_CHANGED) && !stateContext.getTarget().getId().equals(OrderStatusEnum.INIT)) {
Object orderId = ((Map) stateContext.getMessageHeaders().get("paramMap")).get("orderId");
System.out.println(Thread.currentThread().getName() + " | 状态机监听器->业务订单号:" + orderId);
}
}
@Override
public void eventNotAccepted(Message<OrderEventEnum> event) {
System.err.println(Thread.currentThread().getName() + " | this event is not accepted ");
}
}
- 状态机发送事件:
@Component
public class StateMachineHandler {
private final Object object = new Object();
@Autowired
private StateMachine<OrderStatusEnum, OrderEventEnum> stateMachine;
public void sendEvent(OrderStatusEnum oldStatus, OrderEventEnum event, Map<String,Object> paramMap){
Message<OrderEventEnum> message = MessageBuilder
.withPayload(event)
.setHeaderIfAbsent("paramMap",paramMap)
.build();
synchronized (object){
if (!stateMachine.getState().getId().equals(oldStatus)){
stateMachine.stop();
List<StateMachineAccess<OrderStatusEnum, OrderEventEnum>> withAllRegions = stateMachine.getStateMachineAccessor().withAllRegions();
for (StateMachineAccess<OrderStatusEnum, OrderEventEnum> stateMachineAccess:withAllRegions) {
stateMachineAccess.resetStateMachine(new DefaultStateMachineContext<>(oldStatus,null,null,null));
}
stateMachine.start();
}
stateMachine.sendEvent(message);
}
}
}
程序运行入口:
@SpringBootApplication
public class StateMachmineApplication implements CommandLineRunner {
public static void main(String[] args) {
SpringApplication.run(StateMachmineApplication.class, args);
}
@Autowired
private StateMachineHandler stateMachineHandler;
@Override
public void run(String... args) {
for (int i = 0; i < 3; i++) {
TreadRun treadRun = new TreadRun();
treadRun.setStateMachineHandler(stateMachineHandler);
Thread thread = new Thread(treadRun);
thread.start();
}
}
}
--------------------------------------------
public class TreadRun implements Runnable {
private StateMachineHandler stateMachineHandler;
@Override
public void run(){
Map<String, Object> paramMap = new HashMap<>();
paramMap.put("orderId", "CBD-" + 10010);
paramMap.put("type", "fruit");
stateMachineHandler.sendEvent(Constant.OrderStatusEnum.INIT, Constant.OrderEventEnum.SKU_NOTICE, paramMap);
try {
Thread.sleep(1);
} catch (InterruptedException e) {
e.printStackTrace();
}
paramMap = new HashMap<>();
paramMap.put("orderId", "CBD-" + 10011);
paramMap.put("type", "fruit");
stateMachineHandler.sendEvent(Constant.OrderStatusEnum.STAMP_ING, Constant.OrderEventEnum.STAMP_SUCCESS, paramMap);
try {
Thread.sleep(1);
} catch (InterruptedException e) {
e.printStackTrace();
}
paramMap = new HashMap<>();
paramMap.put("orderId", "CBD-" + 10013);
paramMap.put("type", "fruit");
stateMachineHandler.sendEvent(Constant.OrderStatusEnum.LOAN_ING, Constant.OrderEventEnum.LOAN_SUCCESS, paramMap);
try {
Thread.sleep(1);
} catch (InterruptedException e) {
e.printStackTrace();
}
paramMap = new HashMap<>();
paramMap.put("orderId", "CBD-" + 10013);
paramMap.put("type", "fruit");
stateMachineHandler.sendEvent(Constant.OrderStatusEnum.RAISING, Constant.OrderEventEnum.SKU_ENOUGH, paramMap);
try {
Thread.sleep(1);
} catch (InterruptedException e) {
e.printStackTrace();
}
paramMap = new HashMap<>();
paramMap.put("orderId", "CBD-" + 10010);
paramMap.put("type", "fruit");
stateMachineHandler.sendEvent(Constant.OrderStatusEnum.STAMP_SUCCESS, Constant.OrderEventEnum.LOAN_ING, paramMap);
try {
Thread.sleep(1);
} catch (InterruptedException e) {
e.printStackTrace();
}
paramMap = new HashMap<>();
paramMap.put("orderId", "CBD-" + 10011);
paramMap.put("type", "fruit");
stateMachineHandler.sendEvent(Constant.OrderStatusEnum.FULL, Constant.OrderEventEnum.ORDER_STAMP, paramMap);
try {
Thread.sleep(1);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
public void setStateMachineHandler(StateMachineHandler stateMachineHandler) {
this.stateMachineHandler = stateMachineHandler;
}
}
- 运行结果:
重点说明几个点:
- StateMachineHandler.sendEvent():改方法每次对状态机进行重置,否则多订单时状态机状态无法流转;
- StateMachineHandler.sendEvent()方法中synchronized 很重要,如果没有这个,多个订单并发时状态会错乱;加入这个synchronized 控制后会导致状态机的吞吐率下降(这个也是我遇到的问题,个人认为是spring statemachine 的一大缺陷);