聊聊Cola-StateMachine轻量级状态机的实现

聊聊,cola,statemachine,轻量级,状态机,实现 · 浏览次数 : 428

小编点评

**UML 图的定义:** ```plantuml @startuml !include "order.puml" Person(order_operator, "用户") -->-> Event(user_close_event); Person(order_machine, "订单处理机") -->-> Machine(order_opera_machine); Machine(order_opera_machine) -->-> Event(order_closed_event); !include "order_closed.puml" Person(user, "用户") -->-> Machine(order_machine); Machine(order_machine) -->-> Event(order_closed_event); Person(order_machine, "订单处理机") -->-> Event(admin_close_event); Machine(order_opera_machine) -->-> Event(admin_close_event); Person(order_machine, "订单处理机") -->-> Event(overtime_close_event); Machine(order_opera_machine) -->-> Event(overtime_close_event); Person(order_machine, "订单处理机") -->-> Event(check_error_close_event); Machine(order_opera_machine) -->-> Event(check_error_close_event); !enduml ``` **程序操作接口:** ```java @RestController @RequestMapping("/order") public class OrderOperaController { @Autowired @Qualifier("orderOperaMachine") StateMachine orderOperaMachine; @RequestMapping("userclose") public Boolean userCloseOrder() { // 将订单状态改为关闭 String machineId = orderOperaMachine.getMachineId(); Order order = Order.builder().orderId(1).orderName("用户").orderStatusEnum(OrderStatusEnum.INIT).build(); OrderStatusEnum orderStatusEnum = orderOperaMachine.fireEvent(OrderStatusEnum.INIT, OrderEvent.USER_CLOSE, order); return true; } // 其他场景的实现省略... } ```

正文

背景

在分析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链路图

image

业务应用示例

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

关闭订单的简单流程图

image

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

image

添加依赖

<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;
    }
}

启动程序

image

安装UML

image

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

image

最后运行下

image

与聊聊Cola-StateMachine轻量级状态机的实现相似的内容:

聊聊Cola-StateMachine轻量级状态机的实现

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

聊聊GLM-4-9B开源模型的微调loss计算

概述 Github官方地址:GLM-4 网上已经有很多关于微调的文章,介绍各种方式下的使用,这里不会赘述。我个人比较关心的是微调时的loss计算逻辑,这点在很多的文章都不会有相关的描述,因为大多数人都是关心如何使用之类的应用层,而不是其具体的底层逻辑,当然咱也说不清太底层的计算。 可了解其它loss

聊聊一个差点被放弃的项目以及近期的开源计划

前言 自从 StarBlog 和 SiteDirectory 之后,我还没写新的关于开源项目的系列,最近又积累了很多想法,正好写一篇博客来总结一下。 关于差点被放弃的项目,就是最近一直在做的单点认证(IdentityServerLite) IdentityServerLite 开发这个项目的起因,是

聊聊 JSON Web Token (JWT) 和 jwcrypto 的使用

哈喽大家好,我是咸鱼。 最近写的一个 Python 项目用到了 jwcrypto 这个库,这个库是专门用来处理 JWT 的,JWT 全称是 JSON Web Token ,JSON 格式的 Token。 今天就来简单入门一下 JWT。 官方介绍:https://jwt.io/introduction

聊聊MySQL是如何处理排序的

在MySQL的查询中常常会用到 order by 和 group by 这两个关键字,它们的相同点是都会对字段进行排序,那查询语句中的排序是如何实现的呢?

聊聊 Linux iowait

哈喽大家好,我是咸鱼。 我们在使用 top 命令来查看 Linux 系统整体 CPU 使用情况的时候,往往看的是下面这一列: %Cpu(s): 0.0 us, 0.0 sy, 0.0 ni,100.0 id, 68.0 wa, 0.0 hi, 0.0 si, 0.0 st 其中,man 手册解释 w

聊聊Mybatis框架原理

好久没有写博客了。最近工作中封装了一个类似ORM框架的东西。大概的原理就是将Excel数据初始化到本地sqlite数据库后,通过json配置文件,对数据库的数据做增删改查等操作。 其实大概的思考了下,就是半ORM框架mybatis的逻辑,只是我们自己封装的简陋蛮多。想想有现成的轮子没用,反而是自己写

聊聊Spring的工厂方法与FactoryBean

概述 工厂方法是比较常见,常用的一种设计模式。FactoryBean是Spring提供的一种Bean注入IOC容器的方式。 工厂方法 在做日常开发时,一般都会避免直接new对象,而且将new的操作丢给IOC容器,但对于第三方系统的集成,我们不太好直接丢给IOC容器,此时可以通过工厂模式, 提供一个工

聊聊Spring Cloud Alibaba解决方案组件

在java的微服务解决方案中,最先出现目前应用比较多的就是spring cloud netfix系列,但是随着阿里的强劲支持,spring cloud alibaba解决方案逐渐可以替代前者,当然dubbo也是不容小觑的。之前面试几家公司应用的都是spring cloud alibaba,随着我自己

聊聊Spring Cloud Alibaba Sentinel的限流

Spring Cloud Alibaba Sentinel限流功能概览,目前先整理一版,东西有点多,想慢慢打开;后续继续更新......