前面接受了Spring实现的状态机Spring StateMachine
,这个状态机的优点在于功能很完备,缺点也是功能十分完备。
完备到什么程度了,提供了状态机的高级玩法,比如状态的嵌套、状态的并行、子状态机等等。但是在开发中我们并不需要这些。
除此之外,就是性能差的问题,包括但不仅限于Spring StateMachine
在内的所有开源状态机都是有状态的,也就意味着状态机记住先前的状态和输入,以便在进行状态转换时使用这些信息作出决策。这就导致了这些有状态的状态机出现了线程安全的问题。
而我们的系统往往是分布式多线程的,所以为了解决线程安全问题,我们不得不每次都要build一个实例。这就会导致了性能有所下降。
倘若状态机构造十分复杂,并且系统QPS要求又很高的的情况下,肯定出现严重的性能问题。
Cola-StateMachine
组件是一种轻量级的、无状态的、基于注解的状态机实现。可以方便管理订单的状态转换。
COLA框架的状态机使用了连贯接口(Fluent Interfaces)来定义状态和事件,以及对应的动作和检查。
COLA框架的状态机是COLA 4.0应用架构的一部分,旨在控制复杂度,提高开发效率。开发背景可见实现一个状态机引擎,教你看清DSL的本质。
GITHUB地址:alibaba/COLA: COLA: Clean Object-oriented & Layered Architecture (github.com)
Cola-StateMachine
的核心概念如下:
State
:状态Event
:事件,状态由事件触发,引起变化Transition
:流转,表示从一个状态到另一个状态External Transition
:外部流转,两个不同状态之间的流转Internal Transition
:内部流转,同一个状态之间的流转Condition
:条件,表示是否允许到达某个状态Action
:动作,到达某个状态之后,可以做什么StateMachine
:状态机Cola-StateMachine
和其他开源状态机框架最大的一个亮点,就在于解决了性能问题,将状态机变成无状态的。
Cola-StateMachine
为什么是无状态的?首先,我们得知道,为什么其他开源状态机是有状态的,那是因为状态机内部维护了两个状态:初始状态与当前状态。因为这些状态机需要依靠这两个状态来做出决策。
但是
Cola-StateMachine
将这两个状态移除了,也就导致无法获取状态机的初始状态与当前状态。但是实际上我们也并不需要知道,只需要接收目标的状态,然后检查条件,解决执行事件即可,然后返回目标状态,然后根据目标状态更新Order状态即可。这其实就是一个状态流转的DSL表达式,全过程都是无状态的。
StateMachineBuilder
StateMachineBuilder 方法 |
说明 |
---|---|
ExternalTransitionBuilder |
用于一个流转的构建器 |
ExternalTransitionsBuilder |
用于多个流转的构建器 |
InternalTransitionBuilder |
开始构建内部流转 |
StateMachine |
对状态机开始构建,并在StateMachineFactory中注册 |
StateMachine
StateMachine 方法 |
说明 |
---|---|
boolean **verify**(S sourceStateId, E event) |
验证一个事件E是否可以从当前状态S触发 |
S **fireEvent**(S sourceState, E event, C ctx) |
向状态机发送一个事件E,触发状态机,并返回目标状态 |
String **getMachineId**() |
获取状态机的标识符MachineId |
void **showStateMachine**() |
使用访问者模式来显示状态机的结构 |
数据库表字段还是继续使用状态管理艺术——借助Spring StateMachine驭服复杂应用逻辑_起名方面没有灵感的博客-CSDN博客的表字段,还有相关枚举也依然不变,不了解的可以去看看,文章里都有提供了,不再重复说明。
首先,引入依赖
<dependency>
<groupId>com.alibaba.colagroupId>
<artifactId>cola-component-statemachineartifactId>
<version>4.3.1version>
dependency>
编写Cola-StateMachine
配置
/**
* @description: 状态机配置
* @author:lrk
* @date: 2023/9/15
*/
@Configuration
@Slf4j
public class ColaStatemachineConfig {
@Bean
public StateMachine orderStateMachine() {
String ORDER_STATE_MACHINE = "orderStateMachine";
// 第一步:生成一个状态机builder
StateMachineBuilder builder = StateMachineBuilderFactory.create();
// 第二步:定义状态
// 待支付状态->待发货状态 —— 支付
builder.externalTransition() // 外部流转
.from(OrderState.WAIT_PAYMENT) // 起始状态
.to(OrderState.WAIT_DELIVER) // 目标状态
.on(OrderStatusChangeEvent.PAYED) // 事件
.when(checkCondition()) // 流转需要校验的条件,校验不通过不会进行doAction
.perform(doAction()); //执行流转操作 这个action 我们可以按自己所需修改
// 待发货状态->待收货状态 —— 发货
builder.externalTransition()
.from(OrderState.WAIT_DELIVER)
.to(OrderState.WAIT_RECEIVE)
.on(OrderStatusChangeEvent.DELIVERY)
.when(checkCondition())
.perform(doAction());
// 待收货状态-> 完成 —— 收货
builder.externalTransition()
.from(OrderState.WAIT_RECEIVE)
.to(OrderState.FINISH)
.on(OrderStatusChangeEvent.RECEIVED)
.when(checkCondition())
.perform(doAction());
// 创建状态机
StateMachine orderStateMachine = builder.build(ORDER_STATE_MACHINE);
String uml = orderStateMachine.generatePlantUML();
log.info("{}", uml);
return orderStateMachine;
}
private Condition checkCondition() {
return (ctx) -> {
log.info("checkCondition:{}", JSONUtil.toJsonStr(ctx));
return true;
};
}
private Action doAction() {
return (from, to, event, order) -> {
log.info(" 正在操作 " + order.getId() + " from:" + from + " to:" + to + " on:" + event);
// 获取当前订单状态
Integer status = order.getStatus();
// 校验状态是否合法
if (!status.equals(from.getValue())) {
throw new BusinessException(ErrorCode.OPERATION_ERROR);
}
};
}
}
编写Service层
@Service
@Slf4j
public class OrderServiceImpl extends ServiceImpl<OrderMapper, Order>
implements OrderService {
@Resource
private StateMachine<OrderState, OrderStatusChangeEvent, Order> orderStateMachine;
@Override
public Order create() {
Order order = new Order();
order.setName("小明" + UUID.randomUUID());
order.setStatus(OrderState.WAIT_PAYMENT.getValue());
this.save(order);
return order;
}
@Override
public Order pay(int id) {
return orderOperation(id, OrderState.WAIT_PAYMENT, OrderStatusChangeEvent.PAYED);
}
@Override
public Order deliver(int id) {
return orderOperation(id, OrderState.WAIT_DELIVER, OrderStatusChangeEvent.DELIVERY);
}
@Override
public Order receive(int id) {
return orderOperation(id, OrderState.WAIT_RECEIVE, OrderStatusChangeEvent.RECEIVED);
}
@Override
public List<Order> getOrders() {
return this.list();
}
private Order orderOperation(int id, OrderState orderState, OrderStatusChangeEvent orderStatusChangeEvent) {
String machineId = orderStateMachine.getMachineId();
log.info("订单状态机:{}", machineId);
Order order = this.getById(id);
OrderState orderState1 = orderStateMachine.fireEvent(orderState, orderStatusChangeEvent, order);
log.info("订单状态:{}", orderState1);
order.setStatus(orderState1.getValue());
this.updateById(order);
return this.getById(id);
}
}
可以看到,Service层只需要提供当前订单的下状态、订单的事件和订单的详细信息即可由状态机计算出订单的下一个状态,然后完成更新。
前面实战是外部状态的流转(也就是单个起始状态):起始状态为WAIT_PAYMENT
,结束状态为WAIT_DELIVER
,当事件为PAYED
,并且满足checkCondition()
的时候执行doAction()
,成功返回WAIT_DELIVER
,否则返回WAIT_PAYMENT
builder.externalTransition()
.from(OrderState.WAIT_PAYMENT)
.to(OrderState.WAIT_DELIVER)
.on(OrderStatusChangeEvent.PAYED)
.when(checkCondition())
.perform(doAction());
而Cola-StateMachine
还有内部状态流转:这个内部转换发生在**WAIT_PAYMENT
状态下,当发生PAYED
时执行状态流转,当满足checkCondition
()时,执行doAction
,执行成功则返回状态WAIT_PAYMENT
**
builder.internalTransition()
.within(OrderState.WAIT_PAYMENT)
.on(OrderStatusChangeEvent.PAYED)
.when(checkCondition())
.perform(doAction());
除此之外,还有外部状态流转(多个起始状态):起始状态**WAIT_PAYMENT
或WAIT_DELIVER
,结束状态FINISH
,当发生RECEIVED
时执行状态流转,当满足checkCondition
()时,执行doAction
,执行成功则返回状态FINISH
**,否则返回对应起始状态
builder.externalTransitions()
.fromAmong(OrderState.WAIT_PAYMENT, OrderState.WAIT_DELIVER)
.to(OrderState.FINISH)
.on(OrderEvent.RECEIVED)
.when(checkCondition())
.perform(doAction());
参考:
- 管理订单状态,该上状态机吗?轻量级状态机COLA StateMachine保姆级入门教程_cola状态机_倾听铃的声的博客-CSDN博客
- 状态机-cola-DSL_cola 状态机_盖茨比.丁的博客-CSDN博客
- 实现一个状态机引擎,教你看清DSL的本质_张建飞(Frank)的博客-CSDN博客
- alibaba/COLA: COLA: Clean Object-oriented & Layered Architecture (github.com)
- COLA中的cola-statemachine状态机理解与使用例_cola状态机_肇小天的博客-CSDN博客