Spring Statemachine应用实践

前言 在日常开发中经常遇到运营审核经销商活动、任务等等类似业务需求,大部分需求中状态稳定且单一无需使用状态机,但是也会出现大量的if...else前置状态代码,也是不够那么的“优雅” 。随着业务的发展、需求迭代,每一次的业务代码改动都需要维护使用到状态的代码,更让开发人员头疼的是这些维护状态的代码,像散弹一样遍布在各个Service的方法中,不仅增加发布的风险,同时也增加了回归测试的工作量 。
1. 什么是状态机?通常所说的状态机为有限状态机(英语:finite-state machine,缩写:FSM),简称状态机,是表示有限个状态以及在这些状态之间的转移和动作等行为的数学模型 。 
应用FSM模型可以帮助对象生命周期的状态的顺序以及导致状态变化的事件进行管理 。 将状态和事件控制从不同的业务Service方法的if else中抽离出来 。FSM的应用范围很广,状态机 可以描述核心业务规则,核心业务内容. 无限状态机,顾名思义状态无限,类似于“π”,暂不做研究 。
状态机可归纳为4个要素,即现态、条件、动作、次态 。这样的归纳,主要是出于对状态机的内在因果关系的考虑 。“现态”和“条件”是因,“动作”和“次态”是果 。详解如下:
现态:是指当前所处的状态 。
条件:又称为“事件”,当一个条件被满足,将会触发一个动作,或者执行一次状态的迁移 。
动作:条件满足后执行的动作 。动作执行完毕后,可以迁移到新的状态,也可以仍旧保持原状态 。动作不是必需的,当条件满足后,也可以不 执行任何动作,直接迁移到新状态 。
次态:条件满足后要迁往的新状态 。“次态”是相对于“现态”而言的,“次态”一旦被激活,就转变成新的“现态”了 。
动作是在给定时刻要进行的活动的描述 。有多种类型的动作:
进入动作(entry action):在进入状态时进行
退出动作(exit action):在退出状态时进行
输入动作:依赖于当前状态和输入条件进行
转移动作:在进行特定转移时进行
其他术语:
Transition: 状态转移节点,是组成状态机引擎的核心 。
source/from:现态 。
target/to:次态 。
event/trigger:触发节点从现态转移到次态的动作,这里也可能是一个timer 。
guard/when:状态迁移前的校验,执行于action前 。
action:用于实现当前节点对应的业务逻辑处理 。
文字描述比较不容易理解,让我们举个栗子:每天上班都需要坐地铁,从刷卡进站到闸机关闭这个过程,将闸机抽象为一个状态机模型,如下图:
 

Spring Statemachine应用实践

文章插图
 
2. 什么场景使用?以下的场景您可能会需要使用:
您可以将应用程序或其结构的一部分表示为状态 。
您希望将复杂的逻辑拆分为更小的可管理任务 。
应用程序已经遇到了并发问题,例如异步执行导致了一些异常情况 。
当您执行以下操作时,您已经在尝试实现状态机:
使用布尔标志或枚举来建模情况 。
具有仅对应用程序生命周期的某些部分有意义的变量 。
在if...else结构(或者更糟糕的是,多个这样的结构)中循环,检查是否设置了特定的标志或枚举,然后在标志和枚举的某些组合存在或不存在时,做出进一步的异常处理 。
3. 为什么要用?有哪些好处?最初活动模块功能设计时,并没有想使用状态机,仅仅想把状态的变更和业务剥离开,规范状态转换和程序在不同状态下所能提供的能力,去掉复杂的逻辑判断也就是if...else,想换一种模式实现思路,此前了解过spring“全家桶”有状态机就想到了“它”,场景也符合 。
从个人使用的经验,开发阶段和迭代维护期总结了以下几点:
使用状态机来管理状态好处更多体现在代码的可维护性、对于流程复杂易变的业务场景能大大减轻维护和测试的难度 。
解耦,业务逻辑与状态流程隔离,避免业务与状态“散弹式”维护,且状态持久化在同一个事务 。
状态流转越复杂,越能体现状态流转的逻辑清晰,减少的“胶水”代码也越多 。
4. 实践JAVA语言状态机框架有很多,目前Github star 数比较多的有 spring-statemachine(star 1.3K) 、squirrel-foundation(star1.9K)即“松鼠”状态机,stateless4j相较前两个名气较小,未深入研究 。spring-statemachine是spring官方提供的状态机实现,功能强大,但是相对来说很“重”,加载实例的时间也长于squirrel-foundation,不过好在一直都是有更新(目前官方已更新3.2.0),相信会越来越成熟 。


推荐阅读