背景

在分析Seata的saga模式实现时,实在是被其复杂的 json 状态语言定义文件劝退,我是有点没想明白为啥要用这么来实现状态机;盲猜可能是基于可视化的状态机设计器来定制化流程,更方便快捷且上手快吧,毕竟可以通过UI直接操作,设计状态流转图,但我暂时不太能get到。对于Saga模式的实现,之前的博文中已经阐述了基于状态机模式实现Saga,是比较常见且合适的做法,因此了解了下Java中的状态机实现方案,以后有相关的业务场景也可以直接上手使用状态机。

Cola-StateMachine

Cola-StateMachine组件是一种轻量级的、无状态的、基于注解的状态机实现,可以方便地管理订单等业务对象的状态转换。COLA框架的状态机使用了连贯接口(Fluent Interfaces)来定义状态和事件,以及对应的动作和检查。COLA框架的状态机是COLA 4.0应用架构的一部分,旨在控制复杂度,提高开发效率。开发背景可见实现一个状态机引擎,教你看清DSL的本质

基础模型

在Cola-StateMachine组件中有如下的抽象概念模型:

1.State:状态

2.Event:事件,状态由事件触发,引起变化

3.Transition:流转,表示从一个状态到另一个状态

4.External Transition:外部流转,两个不同状态之间的流转

5.Internal Transition:内部流转,同一个状态之间的流转

6.Condition:条件,表示是否允许到达某个状态

7.Action:动作,到达某个状态之后,可以做什么

8.StateMachine:状态机

Cola-StateMachine链路图

业务应用示例

基于订单业务的场景,做一个简单的demo。

关闭订单的简单流程图

关闭订单简单的状态流转图

添加依赖

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

定义一个订单的实体类、订单状态的枚举值、订单事件的枚举值

@Data
@Builder
public class Order { public OrderStatusEnum orderStatusEnum;
public Integer orderId;
public String orderName; } public enum OrderStatusEnum {
INIT("0", "待付款"),
WAITING_FOR_DELIVERY("1", "待发货"),
HAVE_BEEN_DELIVERY("2", "已发货"),
CLOSE("3", "已取消"); private final String code;
private final String info; OrderStatusEnum(String code, String info)
{
this.code = code;
this.info = info;
} public String getCode()
{
return code;
} public String getInfo()
{
return info;
}
} public enum OrderEvent {
/**
* 用户关闭
*/
USER_CLOSE("0", "用户取消"),
/**
* 管理员关闭
*/
ADMIN_CLOSE("1", "后台取消"),
/**
* 超时关闭
*/
OVERTIME_CLOSE("2", "超时取消"),
/**
* 检查错误关闭
*/
CHECK_ERROR_CLOSE("3", "上级审核取消"), /**
* 用户付费
*/
USER_PAY("4", "用户支付"); /**
* 密码
*/
private final String code;
/**
* 信息
*/
private final String info; /**
* 订单事件
*
* @param code 密码
* @param info 信息
*/
OrderEvent(String code, String info) {
this.code = code;
this.info = info;
} /**
* 获取代码
*
* @return {@link String}
*/
public String getCode() {
return code;
} /**
* 获取信息
*
* @return {@link String}
*/
public String getInfo() {
return info;
}
}

在容器启动的时候注册一个订单状态变更的工厂

@Component
public class StateMachineBuilderConfig {
@Autowired
UserCloseAction userCloseAction; @Bean("orderOperaMachine")
public StateMachine orderOperaMachine() {
String ORDER_OPERA = "order_opera";
StateMachineBuilder<OrderStatusEnum, OrderEvent, Order> builder = StateMachineBuilderFactory.create();
//订单从初始化状态-待发货-状态-转到-关闭订单状态--用户关闭
builder.externalTransitions()
.fromAmong(OrderStatusEnum.INIT, OrderStatusEnum.WAITING_FOR_DELIVERY)
.to(OrderStatusEnum.CLOSE)
.on(OrderEvent.USER_CLOSE)
.when(checkCondition())
.perform(userCloseAction);
//订单从-初始化状态-已发货-待发货--转到-关闭订单状态--后台操作人员关闭
builder.externalTransitions()
.fromAmong(OrderStatusEnum.INIT, OrderStatusEnum.HAVE_BEEN_DELIVERY, OrderStatusEnum.WAITING_FOR_DELIVERY)
.to(OrderStatusEnum.CLOSE)
.on(OrderEvent.ADMIN_CLOSE)
.when(checkCondition())
.perform(doAction());
//订单从等待发货状态-转为-订单关闭状态-超时关闭
builder.externalTransition()
.from(OrderStatusEnum.WAITING_FOR_DELIVERY)
.to(OrderStatusEnum.CLOSE)
.on(OrderEvent.OVERTIME_CLOSE)
.when(checkCondition())
.perform(doAction());
//订单从待发货状态--转为-订单关闭状态-上级审批不通过关闭
builder.externalTransition()
.from(OrderStatusEnum.WAITING_FOR_DELIVERY)
.to(OrderStatusEnum.CLOSE)
.on(OrderEvent.CHECK_ERROR_CLOSE)
.when(checkCondition())
.perform(doAction());
//订单从初始化状态--转为待发货状态--用户支付完毕动
builder.externalTransition()
.from(OrderStatusEnum.INIT)
.to(OrderStatusEnum.WAITING_FOR_DELIVERY)
.on(OrderEvent.USER_PAY)
.when(checkCondition())
.perform(doAction()); StateMachine orderOperaMachine = builder.build(ORDER_OPERA); //打印uml图
String plantUML = orderOperaMachine.generatePlantUML();
System.out.println(plantUML);
return orderOperaMachine;
} private Condition<Order> checkCondition() {
return (ctx) -> {
return true;
};
} private Action<OrderStatusEnum, OrderEvent, Order> doAction() {
return (from, to, event, ctx) -> {
System.out.println(ctx.getOrderName() + " 正在操作 " + ctx.getOrderId() + " from:" + from + " to:" + to + " on:" + event);
};
} }

在定义一个特殊的,只是举个例子,可以通过集成的方式集成实现一个用户关单的具体操作

@Component
public class UserCloseAction implements Action<OrderStatusEnum, OrderEvent, Order> { @Override
public void execute(OrderStatusEnum from, OrderStatusEnum to, OrderEvent event, Order context) {
System.out.println("用户关闭流程开始走了");
System.out.println("从这个状态-【" + from.getInfo() + "】-转为+【" + to.getInfo() + "】 的状态");
System.out.println("上下文信息:" + context.toString());
System.out.println("中间执行的一些操作.......");
System.out.println("用户关闭流程完毕了");
}
}

定义一个 controller 的操作接口

@RestController
public class OrderOperaController { @Autowired
@Qualifier("orderOperaMachine")
StateMachine<OrderStatusEnum, OrderEvent, Order> orderOperaMachine; /**
* 场景1-用户关闭订单
*
* @return {@link Boolean}
*/
@RequestMapping("userclose")
public Boolean userCloseOrder() {
//把订单状态改为关闭
String machineId = orderOperaMachine.getMachineId();
System.out.println(machineId);
Order order = Order.builder().orderId(1).orderName("用户").orderStatusEnum(OrderStatusEnum.INIT).build();
OrderStatusEnum orderStatusEnum = orderOperaMachine.fireEvent(OrderStatusEnum.INIT,OrderEvent.USER_CLOSE, order);
System.out.println(orderStatusEnum.toString());
return true;
} /**
* 场景2-管理员关闭订单
*
* @return {@link Boolean}
*/
@RequestMapping("adminClose")
public Boolean adminCloseOrder() {
//把订单状态改为关闭
Order order = Order.builder().orderId(1).orderName("后台操作人员").orderStatusEnum(OrderStatusEnum.HAVE_BEEN_DELIVERY).build();
OrderStatusEnum orderStatusEnum = orderOperaMachine.fireEvent(OrderStatusEnum.HAVE_BEEN_DELIVERY, OrderEvent.ADMIN_CLOSE, order);
System.out.println(orderStatusEnum.toString()); return true;
} /**
* 场景3-超时关闭订单
*
* @return {@link Boolean}
*/
@RequestMapping("overTimeclose")
public Boolean overTimeCloseOrder() {
//把订单状态改为关闭
Order order = Order.builder().orderId(1).orderName("超时了关闭订单")
.orderStatusEnum(OrderStatusEnum.WAITING_FOR_DELIVERY).build();
//OrderStatusEnum orderStatusEnum = orderOperaMachine.fireEvent(OrderStatusEnum.CLOSE, OrderEvent.OVERTIME_CLOSE, order);
OrderStatusEnum orderStatusEnum = orderOperaMachine.fireEvent(OrderStatusEnum.WAITING_FOR_DELIVERY, OrderEvent.OVERTIME_CLOSE, order);
System.out.println(orderStatusEnum.toString());
return true;
} /**
* 场景4-检查错误关闭订单
*
* @return {@link Boolean}
*/
@RequestMapping("checkErrorClose")
public Boolean checkErrorCloseOrder() {
//把订单状态改为关闭
Order order = Order.builder().orderId(1).orderName("上级检查错误").orderStatusEnum(OrderStatusEnum.WAITING_FOR_DELIVERY).build();
OrderStatusEnum orderStatusEnum = orderOperaMachine.fireEvent(OrderStatusEnum.WAITING_FOR_DELIVERY, OrderEvent.CHECK_ERROR_CLOSE, order);
System.out.println(orderStatusEnum.toString());
return true;
}
}

启动程序

安装UML

随便新建一个uml文件,然后将启动程序的控制台输出内容复制到uml中

最后运行下

聊聊Cola-StateMachine轻量级状态机的实现的更多相关文章

  1. 管理订单状态,该上状态机吗?轻量级状态机COLA StateMachine保姆级入门教程

    前言 在平常的后端项目开发中,状态机模式的使用其实没有大家想象中那么常见,笔者之前由于不在电商领域工作,很少在业务代码中用状态机来管理各种状态,一般都是手动get/set状态值.去年笔者进入了电商领域 ...

  2. Spring Boot 揭秘与实战(七) 实用技术篇 - StateMachine 状态机机制

    文章目录 1. 环境依赖 2. 状态和事件 2.1. 状态枚举 2.2. 事件枚举 3. 状态机配置4. 状态监听器 3.1. 初始化状态机状态 3.2. 初始化状态迁移事件 5. 总结 6. 源代码 ...

  3. 状态机引擎在vivo营销自动化中的深度实践 | 引擎篇02

    本文是<vivo营销自动化技术解密>的第3篇文章,分析了营销自动化业务背景和状态机引入原因.状态机的三种基本实现方式,同时介绍了几种业界流行的开源状态机框架实现和特点,以及在项目开发过程中 ...

  4. JavaScript状态模式及状态机模型

    这是一篇,我自己都看不完的文章... 文章大体就两部分: 状态模式的介绍 状态机模型的函数库javascript-state-machine的用法和源码解析 场景及问题背景: 我们平时开发时本质上就是 ...

  5. 通过spring statemmachine 自定义构建属于自己的状态机(两种方式)

    spring 的stateMachine 相对于当前的版本,还是比较新颖的,但是对于合适的业务场景,使用起来还是十分的方便的.但是对于官网提供的文档,讲解的是十分的精简,要想更深入的了解其内部架构,只 ...

  6. 有用的.NET库

    1. Polly,重试 Polly is a .NET 3.5 / 4.0 / 4.5 / PCL library that allows developers to express transien ...

  7. 【转】Android4.4 之Bluetooth整理

    原文网址:http://www.cnblogs.com/shed/p/3737016.html Android 4.4上蓝牙协议栈采用的是BRCM和Google共同开发的bluedroid,代替了之前 ...

  8. Async 与 Await 关键字研究

    1        Aynsc 和 Await 关键字的研究 在 .NET 4.0 以后,基于 Task 的异步编程模式大行其道,因其大大简化了异步编程所带来的大量代码工作而深受编程人员的欢迎,如果你曾 ...

  9. 编写你的第一个 Java 版 Raft 分布式 KV 存储

    前言 本文旨在讲述如何使用 Java 语言实现基于 Raft 算法的,分布式的,KV 结构的存储项目.该项目的背景是为了深入理解 Raft 算法,从而深刻理解分布式环境下数据强一致性该如何实现:该项目 ...

  10. a2dp播放流程源码分析

    之前分析了a2dp profile 的初始化的流程,这篇文章分析一下,音频流在bluedroid中的处理流程. 上层的音频接口是调用a2dp hal 里面的接口来进行命令以及数据的发送的. 关于控制通 ...

随机推荐

  1. TS 基础及在 Vue 中的实践:TypeScript 都发布 5.0 版本啦,现在不学更待何时!

    大家好,我是 Kagol,OpenTiny 开源社区运营,TinyVue 跨端.跨框架组件库核心贡献者,专注于前端组件库建设和开源社区运营. 微软于3月16日发布了 TypeScript 5.0 版本 ...

  2. [BUUCTF]Pwn刷题记录

    本部分内容长期更新,不再创建新文章影响阅读 rip 根据IDA加载入main函数声明发现s数组距离rbp的距离为F,即为15,这里的运行环境是64位,所以应当将Caller's rbp的数据填满,在这 ...

  3. Linux 端口及防火墙常用命令

    Linux 端口及防火墙操作 查看端口操作 一. netstat命令 -t (tcp) 仅显示tcp相关选项 -u (udp)仅显示udp相关选项 -n 拒绝显示别名,能显示数字的全部转化为数字 -l ...

  4. 驱动开发:探索DRIVER_OBJECT驱动对象

    本章将探索驱动程序开发的基础部分,了解驱动对象DRIVER_OBJECT结构体的定义,一般来说驱动程序DriverEntry入口处都会存在这样一个驱动对象,该对象内所包含的就是当前所加载驱动自身的一些 ...

  5. InnoDB 是如何解决幻读的

    前言 大部分人在日常的业务开发中,其实很少去关注数据库的事务相关问题,基本上都是 CURD 一把梭.正好最近在看 MySQL 的相关基础知识,其中对于幻读问题之前一直没有理解深刻,今天就来聊聊「Inn ...

  6. [VMware]常见问题处理

    参考文献 [1] VMware 无法打开虚拟机 该虚拟机似乎正在使用 - 百度经验 [2] 233 http://10.0.8.46:8080/cas/autologin?username=admin ...

  7. CommunityToolkit.Mvvm系列文章导航

    包 CommunityToolkit.Mvvm (又名 MVVM 工具包,以前名为 Microsoft.Toolkit.Mvvm) 是一个现代.快速且模块化的 MVVM 库. 它是 .NET 社区工具 ...

  8. 第138篇:了解HTTP协议(TCP/IP协议,DNS域名解析,浏览器缓存)

    好家伙,发现自己的网络知识十分匮乏,赶紧补一下   这里先举个我生活中的例子 欸,作业不会写了,上网搜一下 用edge浏览器上bing必应搜一下(百度广告太多了,真不想用百度举例子)   假设这是我们 ...

  9. 从零开始基于Archlinux 安装 containerd + k8s

    下载ISO文件:https://mirrors.tuna.tsinghua.edu.cn/archlinux/iso/latest/ 目录 1. 准备工作 2. 磁盘管理 2.1 磁盘分区 2.2 磁 ...

  10. 用C#发送post请求,实现更改B站直播间标题[简单随笔]

    第一次发这样的网络数据包.记录一下. API参考 https://github.com/SocialSisterYi/bilibili-API-collect/blob/master/live/man ...