资讯专栏INFORMATION COLUMN

squirrel-foundation状态机的使用细节

wmui / 930人阅读

摘要:状态机的使用细节上一篇文章介绍了以及三款状态机引擎的实现原理,以及我为何选择作为解决方案。被代理的默认实现为,内部描述了状态机实例创建细节包括类型信息等,同时也包含了的一些全局共享资源包括等。

squirrel-foundation状态机的使用细节

date: 2017-06-19 15:50:18

上一篇文章介绍了stateless4j、spring-statemachine以及squirrel-foundation三款状态机引擎的实现原理,以及我为何选择squirrel-foundation作为解决方案。本文主要介绍一下项目中如何使用squirrel-foundation的一些细节以及如何与spring进行集成。在阅读本文前,建议先阅读官方的使用手册。

生命周期 状态机创建过程

StateMachine: StateMachine实例由StateMachineBuilder创建不被共享,对于使用annotation方式(或fluent api)定义的StateMachine,StateMachine实例即根据此定义创建,相应的action也由本实例执行,与spring的集成最终要的就是讲spring的bean实例注入给由builder创建的状态机实例;

StateMachineBuilder: 本质上是由StateMachineBuilderFactory创建的动态代理。被代理的StateMachineBuilder默认实现为StateMachineBuilderImpl,内部描述了状态机实例创建细节包括State、Event、Context类型信息、constructor等,同时也包含了StateMachine的一些全局共享资源包括StateConverter、EventConverter、MvelScriptManager等。StateMachineBuilder可被复用,使用中可被实现为singleton;

StateMachineBuilderFactory: 为StateMachineBuilder创建的动态代理实例;

事件处理过程

状态正常迁移
TransitionBegin--(exit->transition->entry)-->TransitionComplete-->TransitionEnd

状态迁移异常
TransitionBegin--(exit->transition->entry)-->TransitionException-->TransitionEnd

状态迁移事件拒绝
TransitionBegin-->TransitionDeclined-->TransitionEnd

spring集成

从statemachine的生命流程上可以看到,StateMachineBuilder可以单例方式由spring container管理,而stateMachine的instance的生命周期伴随着请求(或业务)。
从这两点出发,集成spring需要完成两件事:

(1).通过Spring创建StateMachineBuilder实例;

(2).业务函数中通过(1)的StateMachineBuilder实例创建StateMachine实例,并向StateMachine暴露SpringApplicationContext;

泛型参数+覆盖默认构造函数隐藏StateMachineBuilder创建细节,实现ApplicationContextAware接口,接受applicationContext注入,并注入给stateMachine实例。

public abstract class AbstractStateMachineEngine implements ApplicationContextAware {
    protected UntypedStateMachineBuilder stateMachineBuilder = null;
    @SuppressWarnings("unchecked")
    public AbstractStateMachineEngine() {
        //识别泛型参数
        Class genericType = (Class)GenericTypeResolver.resolveTypeArgument(getClass(),
            AbstractStateMachineEngine.class);
        stateMachineBuilder = StateMachineBuilderFactory.create(genericType, ApplicationContext.class);
    }
    //注入applicationContext,并在创建StateMachine实例时注入
    @Override
    public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
        this.applicationContext = applicationContext;
    }
    //delegate fire
    public void fire(int rmaId, State initialState, Trigger trigger, StateMachineContext context) {
        T stateMachine = stateMachineBuilder.newUntypedStateMachine(
                            initialState
                            //暂时开启debug进行日志trace
                            StateMachineConfiguration.create().enableDebugMode(true).enableAutoStart(true),
                            //注入applicationContext
                            applicationContext);
        stateMachine.fire(trigger, context);
    }
    ...
}
@Service
class DiscountRefundStateMachineEngine extends AbstractStateMachineEngine {
}
@Service
public class ReturnGoodsStateMachineEngine extends AbstractStateMachineEngine {
}

StateMachine定义,接受SpringContext注入

@StateMachineParameters(stateType = State.class, eventType = Trigger.class,
    //StateMachineContext 自定义上下文,用来传递数据
    contextType = StateMachineContext.class)
@States({
    @State(name = "PENDING", initialState = true),
    @State(name = "CONFIRMING"),
    @State(name = "REJECTED"),
    @State(name = "REFUND_APPROVING"),
    @State(name = "REFUND_APPROVED"),
    @State(name = "REFUND_FINISHED")
})
@Transitions({
    @Transit(from = "PENDING", to = "CONFIRMING", on = "APPLY_CONFIRM",
        callMethod = "doSomething"),
    @Transit(from = "CONFIRMING", to = "REJECTED", on = "REJECT"),
    @Transit(from = "CONFIRMING", to = "REFUND_APPROVING", on = "APPLY_APPROVED"),
    @Transit(from = "REFUND_APPROVING", to = "REFUND_APPROVED", on = "REFUND_APPROVED"),
    @Transit(from = "REFUND_APPROVED", to = "REFUND_FINISHED", on = "REFUND_FINISH_CONFIRM")
})
public class DiscountRefundStateMachine extends AbstractUntypedStateMachine {
    protected ApplicationContext applicationContext;
    //定义构造函数接受ApplicationContext注入([参看New State Machine Instance](http://hekailiang.github.io/squirrel/))
    public DiscountRefundStateMachine(ApplicationContext applicationContext) {
        this.applicationContext = applicationContext;
    }
    public void doSomething(State fromState, State toState, Trigger event,
                         StateMachineContext stateMachineContext) {
         DemoBean demoBean = this.applicationContext.get("demoBean");
         //do something
    }
    ...
}
状态持久化

从StateMachine的事件响应流程中可以看到,TransitionBegin--(exit->transition->entry)-->TransitionComplete-->TransitionEnd,在TransitionComplete发生一个状态已从source迁移到了target状态,所以我选择了在这个时间点进行了状态的持久化(没有选择TransitionEnd做持久化,因为某些场景在持久化完成后还会存在一些外部动作的触发,例如通知第三方系统当前状态已完成变更)。

public class DiscountRefundStateMachine extends AbstractUntypedStateMachine {
    ..
    @Override
    protected void afterTransitionCompleted(Object fromState, Object toState, Object event, Object context) {
        if (context instanceof StateMachineContext && toState instanceof State) {
            StateMachineContext stateMachineContext = (StateMachineContext)context;
            //从上下文中获取需要持久化的数据,例如订单ID等
            Rma rma = stateMachineContext.get(MessageKeyEnum.RMA);
            //持久化
            rma.setStatus((State)toState);
            this.applicationContext.get("rmaRepository").updateRma(rma);
        } else {
            throw new Exception("type not support, context expect " + StateMachineContext.class.getSimpleName() + ", actually "
                    + context.getClass().getSimpleName() + ", state expect " + State.class.getSimpleName()
                    + ", actually "
                    + toState.getClass().getSimpleName());
        }
    }
}
分布式锁+事务

由于StateMachine实例不是由Spring容器创建,所以这个过程中无法通过注解方式开启事务(Spring没有机会去创建事务代理),我采用了编程式事务,在AbstractStateMachineEngine的fire函数中隐式的实现。
AbstractStateMachineEngine#fire

public abstract class AbstractStateMachineEngine implements ApplicationContextAware {
    ...
    public void fire(int rmaId, State initialState, Trigger trigger, StateMachineContext context) {
        JedisLock jedisLock = jedisLockFactory.buildLock(rmaId);
        //争用分布式锁
        if (jedisLock.tryLock()) {
            try {
                T stateMachine = stateMachineBuilder.newUntypedStateMachine(
                                    initialState
                                    //暂时开启debug进行日志trace
                                    StateMachineConfiguration.create().enableDebugMode(true).enableAutoStart(true),
                                    //注入applicationContext
                                    applicationContext);
                DataSourceTransactionManager transactionManager = applicationContext.get("transactionManager")
                DefaultTransactionDefinition def = new DefaultTransactionDefinition();
                def.setPropagationBehavior(TransactionDefinition.PROPAGATION_REQUIRED);
                TransactionStatus status = transactionManager.getTransaction(def);
                try {
                    stateMachine.fire(trigger, context)
                    transactionManager.commit(status);
                } catch (Exception ex) {
                    transactionManager.rollback(status);
                    throw ex;
                }
            } finally {
                jedisLock.release();
            }
        } 
        ...
    }
}
使用graphviz生成状态拓扑图

squirrel statemachine提供了DotVisitor、SCXMLVisitor两种实现方式用于生成状态机描述文件,项目里我选择了graphviz用来做状态拓扑
graphviz gui工具下载
PS:由于squirrel默认的DotVisitorImpl对带中文描述属性的State/Event枚举不友好,我在原有代码上做了一些调整,有类似需求的可以看这里

更多文章请访问我的博客
转载请注明出处

文章版权归作者所有,未经允许请勿转载,若此文章存在违规行为,您可以联系管理员删除。

转载请注明本文地址:https://www.ucloud.cn/yun/67214.html

相关文章

  • 状态机引擎选型

    摘要:状态机引擎选型概念有限状态机是一种用来进行对象行为建模的工具,其作用主要是描述对象在它的生命周期内所经历的状态序列,以及如何响应来自外界的各种事件。状态机的要素状态机可归纳为个要素,即现态条件动作次态。 状态机引擎选型 date: 2017-06-19 15:50:18 概念 有限状态机是一种用来进行对象行为建模的工具,其作用主要是描述对象在它的生命周期内所经历的状态序列,以及如何响应...

    caige 评论0 收藏0
  • 有限状态机学习

    摘要:状态机有三个特征状态总数是有限的。由于有限状态机的这些特征,我们可以把状态转移的过程做成类似这样的状态转移表。 有限状态机是什么 有限状态机(英语:finite-state machine,缩写:FSM)又称有限状态自动机,简称状态机,是表示有限个状态(State)以及在这些状态之间的转移(Transition)和动作(Action)等行为的数学模型。 状态机有三个特征: 状态(St...

    xiao7cn 评论0 收藏0
  • 有限状态机学习

    摘要:状态机有三个特征状态总数是有限的。由于有限状态机的这些特征,我们可以把状态转移的过程做成类似这样的状态转移表。 有限状态机是什么 有限状态机(英语:finite-state machine,缩写:FSM)又称有限状态自动机,简称状态机,是表示有限个状态(State)以及在这些状态之间的转移(Transition)和动作(Action)等行为的数学模型。 状态机有三个特征: 状态(St...

    bbbbbb 评论0 收藏0
  • “数学之美”系列十:有限状态机和地址识别

    地址的识别和分析是本地搜索必不可少的技术,尽管有许多识别和分析地址的方法,最有效的是有限状态机。一个有限状态机是一个特殊的有向图(参见有关图论的系列),它包括一些状态(节点)和连接这些状态的有向弧。下图是一个识别中国地址的有限状态机的简单的例子。每一个有限状态机都有一个启始状态和一个终止状态和若干中间状态。每一条弧上带有从一个状态进入下一个状态的条件。比如,在上图中,当前的状态是省,如果遇到一个词...

    libxd 评论0 收藏0
  • 云计算战争:OpenStack vs. VMware

    摘要:和的云计算功能特点对比正是这个战争或者说趋势的一个生动写照。在设计方面稍占优势,这源于它优秀的文档资料以及便捷易用的部署和管理接口。总而言之,目前调度器将只会对部署虚拟机环节有影响。 在云计算生态系统中,有两种类型的用户需要使用云计算资源:传统型(Traditional IT applications)和在互联网大潮下逐渐崛起云计算应用型(Cloud-aware applications)。...

    李涛 评论0 收藏0

发表评论

0条评论

最新活动
阅读需要支付1元查看
<