探索状态驱动开发的奇妙世界——Cola-StateMachine的介绍与使用

文章目录

  • 1. 前言
  • 2. Cola-StateMachine概述
  • 3. Cola-StateMachine相关API
  • 4. Cola-StateMachine实战
  • 5. 其他

1. 前言

前面接受了Spring实现的状态机Spring StateMachine,这个状态机的优点在于功能很完备,缺点也是功能十分完备。

完备到什么程度了,提供了状态机的高级玩法,比如状态的嵌套、状态的并行、子状态机等等。但是在开发中我们并不需要这些。

除此之外,就是性能差的问题,包括但不仅限于Spring StateMachine在内的所有开源状态机都是有状态的,也就意味着状态机记住先前的状态和输入,以便在进行状态转换时使用这些信息作出决策。这就导致了这些有状态的状态机出现了线程安全的问题。

而我们的系统往往是分布式多线程的,所以为了解决线程安全问题,我们不得不每次都要build一个实例。这就会导致了性能有所下降。

image-20230915233723638

倘若状态机构造十分复杂,并且系统QPS要求又很高的的情况下,肯定出现严重的性能问题。


2. Cola-StateMachine概述

Cola-StateMachine组件是一种轻量级的、无状态的、基于注解的状态机实现。可以方便管理订单的状态转换。

COLA框架的状态机使用了连贯接口(Fluent Interfaces)来定义状态和事件,以及对应的动作和检查。

COLA框架的状态机是COLA 4.0应用架构的一部分,旨在控制复杂度,提高开发效率。开发背景可见实现一个状态机引擎,教你看清DSL的本质。

GITHUB地址:alibaba/COLA: 🥤 COLA: Clean Object-oriented & Layered Architecture (github.com)

Cola-StateMachine的核心概念如下:

  1. State:状态
  2. Event:事件,状态由事件触发,引起变化
  3. Transition:流转,表示从一个状态到另一个状态
  4. External Transition:外部流转,两个不同状态之间的流转
  5. Internal Transition:内部流转,同一个状态之间的流转
  6. Condition:条件,表示是否允许到达某个状态
  7. Action:动作,到达某个状态之后,可以做什么
  8. StateMachine:状态机

image-20230915235042292

Cola-StateMachine和其他开源状态机框架最大的一个亮点,就在于解决了性能问题,将状态机变成无状态的。

Cola-StateMachine为什么是无状态的?

首先,我们得知道,为什么其他开源状态机是有状态的,那是因为状态机内部维护了两个状态:初始状态与当前状态。因为这些状态机需要依靠这两个状态来做出决策。

但是Cola-StateMachine将这两个状态移除了,也就导致无法获取状态机的初始状态与当前状态。但是实际上我们也并不需要知道,只需要接收目标的状态,然后检查条件,解决执行事件即可,然后返回目标状态,然后根据目标状态更新Order状态即可。

这其实就是一个状态流转的DSL表达式,全过程都是无状态的。

image-20230916000108400


3. Cola-StateMachine相关API

StateMachineBuilder

StateMachineBuilder方法说明
ExternalTransitionBuilder<S, E, C> externalTransition()用于一个流转的构建器
ExternalTransitionsBuilder<S, E, C> externalTransitions()用于多个流转的构建器
InternalTransitionBuilder<S, E, C> internalTransition()开始构建内部流转
StateMachine<S, E, C> build(String machineId)对状态机开始构建,并在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**()使用访问者模式来显示状态机的结构

4. Cola-StateMachine实战

数据库表字段还是继续使用状态管理艺术——借助Spring StateMachine驭服复杂应用逻辑_起名方面没有灵感的博客-CSDN博客的表字段,还有相关枚举也依然不变,不了解的可以去看看,文章里都有提供了,不再重复说明。

首先,引入依赖

<dependency><groupId>com.alibaba.cola</groupId><artifactId>cola-component-statemachine</artifactId><version>4.3.1</version>
</dependency>

编写Cola-StateMachine配置

/*** @description: 状态机配置* @author:lrk* @date: 2023/9/15*/
@Configuration
@Slf4j
public class ColaStatemachineConfig {@Beanpublic StateMachine<OrderState, OrderStatusChangeEvent, Order> orderStateMachine() {String ORDER_STATE_MACHINE = "orderStateMachine";// 第一步:生成一个状态机builderStateMachineBuilder<OrderState, OrderStatusChangeEvent, Order> 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<OrderState, OrderStatusChangeEvent, Order> orderStateMachine = builder.build(ORDER_STATE_MACHINE);String uml = orderStateMachine.generatePlantUML();log.info("{}", uml);return orderStateMachine;}private Condition<Order> checkCondition() {return (ctx) -> {log.info("checkCondition:{}", JSONUtil.toJsonStr(ctx));return true;};}private Action<OrderState, OrderStatusChangeEvent, Order> 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);}};}
}

image-20230916001317511

编写Service层

@Service
@Slf4j
public class OrderServiceImpl extends ServiceImpl<OrderMapper, Order>implements OrderService {@Resourceprivate StateMachine<OrderState, OrderStatusChangeEvent, Order> orderStateMachine;@Overridepublic Order create() {Order order = new Order();order.setName("小明" + UUID.randomUUID());order.setStatus(OrderState.WAIT_PAYMENT.getValue());this.save(order);return order;}@Overridepublic Order pay(int id) {return orderOperation(id, OrderState.WAIT_PAYMENT, OrderStatusChangeEvent.PAYED);}@Overridepublic Order deliver(int id) {return orderOperation(id, OrderState.WAIT_DELIVER, OrderStatusChangeEvent.DELIVERY);}@Overridepublic Order receive(int id) {return orderOperation(id, OrderState.WAIT_RECEIVE, OrderStatusChangeEvent.RECEIVED);}@Overridepublic 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层只需要提供当前订单的下状态、订单的事件和订单的详细信息即可由状态机计算出订单的下一个状态,然后完成更新。

image-20230916001114532

image-20230916001149424


5. 其他

前面实战是外部状态的流转(也就是单个起始状态):起始状态为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_PAYMENTWAIT_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());

参考:

  1. 管理订单状态,该上状态机吗?轻量级状态机COLA StateMachine保姆级入门教程_cola状态机_倾听铃的声的博客-CSDN博客
  2. 状态机-cola-DSL_cola 状态机_盖茨比.丁的博客-CSDN博客
  3. 实现一个状态机引擎,教你看清DSL的本质_张建飞(Frank)的博客-CSDN博客
  4. alibaba/COLA: 🥤 COLA: Clean Object-oriented & Layered Architecture (github.com)
  5. COLA中的cola-statemachine状态机理解与使用例_cola状态机_肇小天的博客-CSDN博客

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.hqwc.cn/news/112342.html

如若内容造成侵权/违法违规/事实不符,请联系编程知识网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

ddtrace 系列篇之 dd-trace-java 项目编译

dd-trace-java 是 Datadog 开源的 java APM 框架&#xff0c;本文主要讲解如何编译 dd-trace-java 项目。 环境准备 JDK 编译环境(三个都要&#xff1a;jdk8\jdk11\jdk17) Gradle 8 Maven 3.9 (需要 15G 以上的存储空间存放依赖) Git >2 (低于会出现一想不到的异常&#xf…

【刷题】蓝桥杯

蓝桥杯2023年第十四届省赛真题-平方差 - C语言网 (dotcpp.com) 初步想法&#xff0c;x y2 − z2&#xff08;yz)(y-z) 即xa*b&#xff0c;ayz&#xff0c;by-z 2yab 即ab是2的倍数就好了。 即x存在两个因数之和为偶数就能满足条件。 但时间是&#xff08;r-l&#xff09;*x&am…

js创建动态key的对象ES6和ES5的方法

前提&#xff1a; 有个场景&#xff0c;循环数组&#xff0c;根据每一项的值&#xff0c;往一个数组中push一个新对象&#xff0c;对象的key不同要从数组中获取 情况解析&#xff1a;push没有什么问题&#xff0c;问题就是创建一个动态key的对象。下面就说一下如何以参数为key…

IP风险查询:抵御DDoS攻击和CC攻击的关键一步

随着互联网的普及&#xff0c;网络攻击变得越来越普遍和复杂&#xff0c;对企业和个人的网络安全构成了重大威胁。其中&#xff0c;DDoS&#xff08;分布式拒绝服务&#xff09;攻击和CC&#xff08;网络连接&#xff09;攻击是两种常见且具有破坏性的攻击类型&#xff0c;它们…

【软考复习系列】计算机网络易错知识点记录

参考文章&#xff1a;图解路由器&#xff1a;这玩意儿能连接全世界的网络&#xff1f; - 知乎 (zhihu.com) 宏内核和微内核 宏内核应该叫单内核或者单核。在这种单核的设计中&#xff0c;内核是一个大的整体&#xff0c;所有内核服务都运行在一个地址空间中&#xff0c;函数之…

WPF中DataGrid控件绑定数据源

步骤 创建数据源&#xff1a;首先&#xff0c;我们需要创建一个数据源&#xff0c;可以是一个集合&#xff08;如List、ObservableCollection等&#xff09;&#xff0c;也可以是一个DataTable对象。数据源中的每个元素代表一行数据。 设置DataGrid的ItemsSource属性&#xff…

【ELFK】之消息队列kafka

一、kafka的定义 Kafka 是一个分布式的基于发布/订阅模式的消息队列&#xff08;MQ&#xff0c;Message Queue&#xff09;&#xff0c;主要应用于大数据实时处理领域。Kafka 是最初由 Linkedin 公司开发&#xff0c;是一个分布式、支持分区的&#xff08;partition&#xff0…

华为云云耀云服务器L实例评测|Git 私服搭建指南

前言 本文为华为云云耀云服务器L实例测评文章&#xff0c;测评内容是 云耀云服务器L实例 Git 私有服务器搭建指南 系统配置&#xff1a;2核2G 3M Ubuntu 20.04 我们平时在使用代码托管服务的时候&#xff0c;可能某些代码托管平台对成员有限制&#xff0c;或是由于内容原因会对…

无涯教程-JavaScript - PI函数

描述 PI函数返回数字3.14159265358979,数学常数pi,精确到15位数字。 语法 PI ()争论 PI函数语法没有参数。 适用性 Excel 2007,Excel 2010,Excel 2013,Excel 2016 Example JavaScript 中的 PI函数 - 无涯教程网无涯教程网提供描述PI函数返回数字3.14159265358979,数学常…

python爬虫爬取电影数据并做可视化

思路&#xff1a; 1、发送请求&#xff0c;解析html里面的数据 2、保存到csv文件 3、数据处理 4、数据可视化 需要用到的库&#xff1a; import requests,csv #请求库和保存库 import pandas as pd #读取csv文件以及操作数据 from lxml import etree #解析html库 from …

万字长文总结检索增强 LLM

连接&#xff1a;https://zhuanlan.zhihu.com/p/655272123 ChatGPT 的出现&#xff0c;让我们看到了大语言模型 ( Large Language Model, LLM ) 在语言和代码理解、人类指令遵循、基本推理等多方面的能力&#xff0c;但幻觉问题 Hallucinations[1] 仍然是当前大语言模型面临的一…

【陕西理工大学-数学软件实训】数学实验报告(8)(数值微积分与方程数值求解)

目录 一、实验目的 二、实验要求 三、实验内容与结果 四、实验心得 一、实验目的 1. 掌握求数值导数和数值积分的方法。 2. 掌握代数方程数值求解的方法。 3. 掌握常微分方程数值求解的方法。 二、实验要求 1. 根据实验内容&#xff0c;编写相应的MATLAB程序&#xff0c…