Mini-FSM:超轻量级有限状态机框架,已在Github开源
有限状态机(Finite State Machine,简称FSM),表示有限个状态以及在这些状态之间的转移和动作等行为的处理模型。在任何给定的时间点,有限状态机都处于某一特定状态,并且可以根据当前状态和输入条件,从当前状态转移到另一个状态。有限状态机相关的核心概念主要包括:
有限状态机除了使用状态转移图表示,也可以使用状态转移表呈现,展示基于当前状态和其他输入,要移动到什么状态的表格。例如:当前状态(B)和条件(Y)的组合指示出下一个状态(C)。
当前状态→ 条件↓ | 状态A | 状态B | 状态C |
---|---|---|---|
条件X | … | … | … |
条件Y | … | 状态C | … |
条件Z | … | … | … |
很多调度系统和工作流系统都会内置状态机管理,用于维护状态转移的事件处理。例如,YARN(资源调度系统)将各种处理逻辑抽象为事件和对应的事件调度器。每类事件的处理过程可分割成多个步骤,用有限状态机表示。
处理请求作为事件进入系统,由中央异步调度器(Async-Dispatcher)负责传递给相应事件调度器(Event Handler)。事件调度器可能将事件转发给另外一个事件调度器,也可能转发给一个带有有限状态机的事件处理器,其处理结果也以事件的形式输出给中央异步调度器。而新的事件会再次被中央异步调度器转发给下一个事件调度器,直至达到终止条件才处理完成。
有限状态机有不同的实现方式,具体取决于具体的应用场景,常见的实现方式有:
每个状态都是一个对象,这些对象共享一个公共状态接口。状态机持有一个状态对象的引用,所有的事件处理和动作执行都委托给这个状态对象。当状态转移时,状态机将引用切换到另一个状态对象。
其中,Context和具体状态都可以设置Context的下个状态, 并通过触发变更Context的状态引用实现状态转移。
Spring Statemachine (SSM) 是一个框架,允许在Spring 应用程序中使用传统的状态机概念。SSM 提供以下功能:
更多详情可查看:官方文档
Cola:Clean Object-Oriented and Layered Architecture的缩写,代表“整洁面向对象分层架构”。COLA分为两个部分,COLA架构和COLA组件,其中StateMachine组件实现了有限状态机功能。
Cola 参考Spring Statemachine设计思想进行简化,有限状态机的核心概念主要包括:
更多详情可查看:官方说明文档
常见的其他状态机开源框架有:
在业务功能实现时,遇到状态流转的管理。针对状态机进行选择,遇到几个问题:
Cola StateMachine的状态机本身是无状态(Stateless)的,且实现较简洁,因此选择基于Cola StateMachine 和 Spring StateMachine 的设计思想进行简化。在系统内部内嵌了简单的有限状态机管理,并根据业务需求扩展了Action执行后的返回结果ActionResult。其中Action的执行使用响应式编程,基于Flux 和 Mono 实现。
简化的Mini-FSM 有限状态机的主要接口定义包括:
Mini-FSM框架实现如下所示:用户基于Event事件触发当前状态转移并返回转移结果。
Transition流转类型分为两类:
更多可参考Mini-FSM中的单元测试类StateMachineTest。
可自定义使用枚举类定义State状态 和 Event事件
enum State {
S1, S2, S3
}
enum Event {
E1, E23, E22, E13
}
本示例中状态机配置定义包括:
Builder<State, Event> builder = StateMachineBuilder.builder();
// FSM状态变换定义
builder.configureTransitions()
.withInternal() //1.构建内部流转 S1
.source(State.S1)
.event(Event.E1)
.action(context -> {
sleep(2000);
assert context.getEvent() == Event.E1;
return ActionResult.of();
})
.and()
.withExternal() //2.构建外部流转 S2 → S3
.source(State.S2)
.target(State.S3)
.event(Event.E23)
.action(context -> {
assert context.getEvent() == Event.E23;
MessageHeaders headers = context.getMessage().getHeaders();
String result = ((String) headers.get("info")) + context.getEvent();
return ActionResult.of(ImmutableMap.of("result", result));
})
.and()
.withInternal()
.source(State.S1)
.event(Event.E13) //3.构建内部流转 S1, 模拟异常回滚
.action(
context -> {
System.out.println("mock failed case");
throw new RuntimeException("event e13 error");
},
context -> {
System.out.println("Drop table Rollback");
return ActionResult.of(ImmutableMap.of("result", "drop table back"));
})
.and()
.withInternal()
.source(State.S3)
.event(Event.E22)
.guard(context -> context.getEvent() == Event.E13) //4.构建内部流转S3,模拟guard失败
.action(context -> ActionResult.of());
//构建FSM状态机
StateMachine<State, Event> stateMachine = builder.build();
(1). 单个Event事件执行,没有返回结果场景
MessageHeaders headers = new MessageHeaders(ImmutableMap.of("info", "123456"));
final StateMachineEventResult<State, Event> inner =
stateMachine.sendEvent(Message.of(State.S1, Event.E1, headers));
assert inner.getResultType() == ResultType.ACCEPTED;
assert inner.getState() == State.S1;
(2). 单个Event事件执行,具有返回执行结果
final StateMachineEventResult<State, Event> external =
stateMachine.sendEvent(Message.of(State.S2, Event.E23, headers));
assert external.getState() == State.S3;
ActionResult actionResult = external.getActionResults().iterator().next();//获取结果
assertEquals("123456E23", actionResult.infos().get("result"));
(3). 多个Event事件 同步执行,默认执行方式
List<StateMachineEventResult<State, Event>> results = stateMachine.sendEvents(
ImmutableList.of(
Message.of(State.S1, Event.E1, headers),
Message.of(State.S2, Event.E23, headers)));
assert results.stream().noneMatch(result -> result.getResultType() == ResultType.DENIED);
(4). 多个Event事件 异步执行
List<StateMachineEventResult<State, Event>> async = stateMachine.sendEvents(
ImmutableList.of(
Message.of(State.S1, Event.E1, headers),
Message.of(State.S2, Event.E23, headers)),
true); //指定多个事件异步执行
assert async.stream().noneMatch(result -> result.getResultType() == ResultType.DENIED);
(5). 失败事件执行,结果类型为DENIED
final StateMachineEventResult<State, Event> failed =
stateMachine.sendEvent(Message.of(State.S1, Event.E13, headers));
assert failed.getResultType() == ResultType.DENIED;
(6). 多个事件执行,事件之间相互隔离,失败事件不影响其他事件执行
results = stateMachine.sendEvents(
ImmutableList.of(
Message.of(State.S1, Event.E13, headers),
Message.of(State.S1, Event.E1, headers),
Message.of(State.S2, Event.E23, headers)));
assert results.size() ==3;
//事件失败的数量=1
assert results.stream().filter(result -> result.getResultType() == ResultType.DENIED).count() == 1;
//事件成功的数量=2
assert results.stream().filter(result -> result.getResultType() == ResultType.ACCEPTED).count() == 2;
(7). Guad校验不通过
StateMachineEventResult<State, Event> guard =
stateMachine.sendEvent(Message.of(State.S3, Event.E22, headers));
assert guard.getResultType() == ResultType.DENIED;
有限状态机对于复杂的多状态流转管理是有效的,但状态机的执行流程引入会增加系统的复杂性和提升维护难度,因此不能滥用。对于简单的状态流转,例如只有3个状态变换,且执行操作单一,更建议直接使用Switch Case/枚举实现。最后,希望通过本文可了解到FSM框架的一些设计思路。
原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。
如有侵权,请联系 cloudcommunity@tencent.com 删除。
原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。
如有侵权,请联系 cloudcommunity@tencent.com 删除。