COLA下的cola-statemachine状态机

相比Spring statemachine状态机等的复杂,功能多;我们更需要常用的功能,简单使用,所以这类就显得不简洁;再看cola-statemachine相比就是小巧、无状态、简单、轻量、性能极高的状态机DSL实现,解决业务中的状态流转问题。
github:
https://github.com/alibaba/COLA/tree/master/cola-components/cola-component-statemachine

概念:
State:状态

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

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

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

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

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

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

StateMachine:状态机

外部过程描述:起始状态STATE1,结束状态STATE2,当发生EVENT1时执行状态转移,当满足checkCondition()时,执行doAction,执行成功则返回状态STATE2,否则返回STATE1。

public class StateMachineTest {

    static String MACHINE_ID = "TestStateMachine";

    static enum States {
        STATE1, STATE2, STATE3, STATE4
    }

    static enum Events {
        EVENT1, EVENT2, EVENT3, EVENT4, INTERNAL_EVENT
    }

    static class Context{
        String operator = "frank";
        String entityId = "123465";
    }

    @Test
    public void testExternalNormal(){
        // 第一步:生成一个状态机builder
        StateMachineBuilder<States, Events, Context> builder = StateMachineBuilderFactory.create();
        // 第二步:设置一个外部状态转移类型的builder,并设置from\to\on\when\perform
        builder.externalTransition()
                .from(States.STATE1)
                .to(States.STATE2)
                .on(Events.EVENT1)
                .when(checkCondition())
                .perform(doAction()); //这个action 我们可以按自己所需修改,比如这种Action<R,T> service的方法Service::method
 
        // 第三步:设置状态机的id和ready,并在StateMachineFactory中的stateMachineMap进行注册
        StateMachine<States, Events, Context> stateMachine = builder.build(MACHINE_ID);
        // 第四步:触发状态机
        States target = stateMachine.fireEvent(States.STATE1, Events.EVENT1, new Context());
        Assert.assertEquals(States.STATE2, target);
    }
}

源码分析:这里只是简单介绍,整个流转的链路,因为具体的代码还是要根据自己的知识储备理解;
第一步:生成一个状态机builder
new com.alibaba.cola.statemachine.builder.StateMachineBuilderImpl

    /**
     * StateMap is the same with stateMachine, as the core of state machine is holding reference to states.
     */
    private final Map<S, State< S, E, C>> stateMap = new ConcurrentHashMap<>();
    private final StateMachineImpl<S, E, C> stateMachine = new StateMachineImpl<>(stateMap);

第二步:设置一个外部状态转移类型的builder,并设置from\to\on\when\perform

@Override
    public ExternalTransitionBuilder<S, E, C> externalTransition() {
        return new TransitionBuilderImpl<>(stateMap, TransitionType.EXTERNAL);
    }

TransitionBuilderImpl为多实现接口实现类,这个设计加上不同接口的唯一方法实现,可以让from to ,按DSL方式运行

class TransitionBuilderImpl<S,E,C> implements ExternalTransitionBuilder<S,E,C>, InternalTransitionBuilder<S,E,C>, From<S,E,C>, On<S,E,C>, To<S,E,C> {

image.png

上述构建就是一个状态流转的过程

class TransitionBuilderImpl<S,E,C> implements ExternalTransitionBuilder<S,E,C>, InternalTransitionBuilder<S,E,C>, From<S,E,C>, On<S,E,C>, To<S,E,C> {
    //状态机
    final Map<S, State<S, E, C>> stateMap;
    //原状态
    private State<S, E, C> source;
    //目标状态
    protected State<S, E, C> target;
    //该具体做事流转
    private Transition<S, E, C> transition;
     //流转类型
    final TransitionType transitionType;
...
    // 此时只是把from的state新增到stateMap中,返回结果赋给本地变量source
    @Override
    public From<S, E, C> from(S stateId) {
        source = StateHelper.getState(stateMap, stateId);
        return this;
    }
 
    // 此时只是把to的state新增到stateMap中,返回结果赋给本地变量target
    @Override
    public To<S, E, C> to(S stateId) {
        target = StateHelper.getState(stateMap, stateId);
        return this;
    }
  //所以ON是在Form和to后面执行,因为它用本流程中的source,向来源state中加入Transition
  //Transition是一个具体做事的流转,其中包含事件,目标状态和流转类型
  //看下面源码就是一个事件对应Transition
  @Override
    public On<S, E, C> on(E event) {
        transition = source.addTransition(event, target, transitionType);
        return this;
    }
//该执行流转的条件
@Override
    public When<S, E, C> when(Condition<C> condition) {
        transition.setCondition(condition);
        return this;
    }
//设置transition的action
 @Override
    public void perform(Action<S, E, C> action) {
        transition.setAction(action);
    }

...
}

第三步:
builder.build(MACHINE_ID) 此步就是状态机已构建完成,一般直接@Bean注入,使用时直接调用


public class StateMachineBuilderImpl<S, E, C> implements StateMachineBuilder<S, E, C> {
 
...
 
    private final StateMachineImpl<S, E, C> stateMachine = new StateMachineImpl<>(stateMap);
 
...
 
    // 设置状态机的id和ready,并在StateMachineFactory中的stateMachineMap进行注册
    @Override
    public StateMachine<S, E, C> build(String machineId) {
        stateMachine.setMachineId(machineId);
        stateMachine.setReady(true);
        StateMachineFactory.register(stateMachine);
        return stateMachine;
    }
 
}

第四步:States target = stateMachine.fireEvent(States.STATE1, Events.EVENT1, new Context()); 业务具体使用时,这个方法入参是原状态和事件和action的入参,这个就按上面配置的状态机进行流转;from to on when perform

public class StateMachineImpl<S,E,C> implements StateMachine<S, E, C> {

    private String machineId;

    private final Map<S, State<S,E,C>> stateMap;

    private boolean ready;

    public StateMachineImpl(Map<S, State< S, E, C>> stateMap){
        this.stateMap = stateMap;
    }

    public S fireEvent(S sourceStateId, E event, C ctx){
        isReady();
        State sourceState = getState(sourceStateId);
        return doTransition(sourceState, event, ctx).getId();
    }

    private State<S, E, C> doTransition(State sourceState, E event, C ctx) {
        //根据原状态和事件获取流转transition
        Optional<Transition<S,E,C>> transition = sourceState.getTransition(event);
        if(transition.isPresent()){
            //ctx为执行入参
            return transition.get().transit(ctx);
        }
        Debugger.debug("There is no Transition for " + event);
        return sourceState;

Transition是具体做事的流转

/**
 * TransitionImpl。
 *
 * This should be designed to be immutable, so that there is no thread-safe risk
 *
 * @author Frank Zhang
 * @date 2020-02-07 10:32 PM
 */
public class TransitionImpl<S,E,C> implements Transition<S,E,C> {

    private State<S, E, C> source;

    private State<S, E, C> target;

    private E event;

    private Condition<C> condition;

    private Action<S,E,C> action;

    private TransitionType type = TransitionType.EXTERNAL;
    
...
 @Override
    public State<S, E, C> transit(C ctx) {
        Debugger.debug("Do transition: "+this);
        this.verify();
        if(condition == null || condition.isSatisfied(ctx)){
            if(action != null){
                action.execute(source.getId(), target.getId(), event, ctx);
            }
            return target;
        }

        Debugger.debug("Condition is not satisfied, stay at the "+source+" state ");
        return source;
    }
...
    @Override
    public final String toString() {
        return source + "-[" + event.toString() +", "+type+"]->" + target;
    }

    @Override
    public boolean equals(Object anObject){
        if(anObject instanceof Transition){
            Transition other = (Transition)anObject;
            if(this.event.equals(other.getEvent())
                    && this.source.equals(other.getSource())
                    && this.target.equals(other.getTarget())){
                return true;
            }
        }
        return false;
    }

    @Override
    public void verify() {
        if(type== TransitionType.INTERNAL && source != target) {
            throw new StateMachineException(String.format("Internal transition source state '%s' " +
                    "and target state '%s' must be same.", source, target));
        }
    }

State是整个流转中状态类

public interface State<S,E,C> extends Visitable{

    /**
     * Gets the state identifier.
     *
     * @return the state identifiers
     */
    S getId();

    /**
     * Add transition to the state
     * @param event the event of the Transition
     * @param target the target of the transition
     * @return
     */
    Transition<S,E,C> addTransition(E event, State<S, E, C> target, TransitionType transitionType);

    Optional<Transition<S,E,C>> getTransition(E event);

    Collection<Transition<S,E,C>> getTransitions();

}
public class StateImpl<S,E,C> implements State<S,E,C> {
    protected final S stateId;
    private HashMap<E, Transition<S, E,C>> transitions = new HashMap<>();

    StateImpl(S stateId){
        this.stateId = stateId;
    }

    @Override
    public Transition<S, E, C> addTransition(E event, State<S,E,C> target, TransitionType transitionType) {
        Transition<S, E, C> newTransition = new TransitionImpl<>();
        newTransition.setSource(this);
        newTransition.setTarget(target);
        newTransition.setEvent(event);
        newTransition.setType(transitionType);

        Debugger.debug("Begin to add new transition: "+ newTransition);
        verify(event, newTransition);
        transitions.put(event, newTransition);
        return newTransition;
    }
 /**
     * @param event
     * @param newTransition
     */
    private void verify(E event, Transition<S,E,C> newTransition) {
        Transition existingTransition = transitions.get(event);
        if(existingTransition != null){
            if(existingTransition.equals(newTransition)){
                throw new StateMachineException(existingTransition+" already Exist, you can not add another one");
            }
        }
    }

最后用层级展示下源码的设计:存储下来类似树形分支
第一维度:状态
第二维度:原状态 对应 事件
第三维度:事件对应具体流转


image.png

推荐阅读更多精彩内容

  • 这几天想研究下状态机,所以开始阅读了squirrel的代码,文档提到了建议先学习下状态机基础知识。所以学习了这方面...
    捞月亮的阿汤哥阅读 354评论 0 0
  • 1、多个状态机的搞法    在实际的企业应用中,基本不可能只有一个状态机流程在跑,比如订单,肯定是很多个订单在运行...
    张晨辉Allen阅读 1,672评论 0 2
  • 『代码github地址』 标签: 有限状态机,Akka fsm,squirrel-foundation,java状...
    醉叁重阅读 24,913评论 3 24
  • 这个系列最终实现的状态机并不是一个标准的状态机,把状态机的很多标准的概念进行了简化,对概念的东西做了减法,实现了具...
    iPolaris阅读 33,093评论 2 39
  • 大家好,我是IT修真院深圳分院第01期学员,一枚正直纯洁善良的web程序员。 今天给大家分享一下,修真院官网JS(...
    老菜菜阅读 3,520评论 0 1
  • 久违的晴天,家长会。 家长大会开好到教室时,离放学已经没多少时间了。班主任说已经安排了三个家长分享经验。 放学铃声...
    飘雪儿5阅读 5,387评论 16 21
  • 今天感恩节哎,感谢一直在我身边的亲朋好友。感恩相遇!感恩不离不弃。 中午开了第一次的党会,身份的转变要...
    迷月闪星情阅读 7,960评论 0 9
  • 可爱进取,孤独成精。努力飞翔,天堂翱翔。战争美好,孤独进取。胆大飞翔,成就辉煌。努力进取,遥望,和谐家园。可爱游走...
    赵原野阅读 1,487评论 1 1
  • 在妖界我有个名头叫胡百晓,无论是何事,只要找到胡百晓即可有解决的办法。因为是只狐狸大家以讹传讹叫我“倾城百晓”,...
    猫九0110阅读 1,424评论 2 3