前言


在大型的分布式系统中,经常会涉及到状态的改变,这里的状态变化可以分很多种,最极端的情况是,任何状态之间都可以互相切换。这种状态之间的切换,转变,更加官方一点的称为叫状态机。这个词可能很多人会感到比较陌生,英文就是State Machine。所以如果大家在学习开源项目中,看到这个单词,指的就是状态机的意思。那么状态机有什么用途呢,为什么我们要定义这样一个概念呢?本文笔者就来简单聊聊状态机的管理。

状态机有何用


我们老是说状态机,状态机,那么它到底有何用呢?答案很简单,5个字:状态机管理。很明显,它的用途在于状态的管理。可能有些人会觉得奇怪,为什么需要引入状态机的概率来管理状态呢,一种简单的做法是在特定的条件下特定状态的更新,不就OK了吗。比如说下面这种方式:

    switch(State s)case s1:s.setState(NewState1)case s2:s.setState(NewState2)case s3:s.setState(NewState3)...

以上这种通过if语句或switch语句在条件判断,然后进行状态改变的逻辑,从原理上来说并没有错。但是大家想一想,如果系统够复杂,中间涉及的状态足够多,而且每个不同状态切换所需要的判定条件(这里可理解为触发的event事件)又不同,那么这种方式会带来很大的弊端,

状态多的情况下容易出错,而且维护成本很高,如果未来要更改状态规则的时候,无法全局修改。无法清晰了解整个状态变化过程。

所以在拥有复杂状态变化的情况下,我们一般的做法都是引入状态机管理对象,来专门做状态的更新。那么我们如何定义状态机管理对象呢?接着往下看。

状态机对象如何定义


在定义这个类对象之前,一般我们要先设计好系统的状态变化图,里面应该包含2大元素信息:

  • 1.系统所有可能出现的状态
  • 2.不同状态间进行切换的触发事件(event)。

这里笔者以Ozone中的Container状态机管理为例,如下所示:

  // Client-driven Create State Machine// States: <ALLOCATED>------------->CREATING----------------->[OPEN]// Events:            (BEGIN_CREATE)    |    (COMPLETE_CREATE)//                                      |//                                      |(TIMEOUT)//                                      V//                                  DELETING----------------->[DELETED]//                                           (CLEANUP)

我们可以看到上面总共有5个状态,4个event事件,对应着4种状态关系的切换。我们摘取其中一段:

 <ALLOCATED>------------->CREATING----------------->[OPEN](BEGIN_CREATE)    |    (COMPLETE_CREATE)

这里包含了2种切换。1.ALLOCATED分配状态经过BEGIN_CREATE事件后,状态变为CREATING状态。2.然后收到创建完成的事件COMPLETE_CREATE,状态又从CREATING变为OPEN。

但是单纯看图形的描述,当然很好理解,下面我们来想想如何在代码层面进行实现呢?

首先第一步,定义好所有的状态和相应的事件,这个我们一般会用枚举来做。OK,这一步很简单,大家肯定都会。继续下一步。

定义状态机管理类,继续以Ozone的Container状态机管理为例,代码如下:

/*** Template class that wraps simple event driven state machine.* @param <STATE> states allowed* @param <EVENT> events allowed*/
public class StateMachine<STATE extends Enum<?>, EVENT extends Enum<?>> {// 系统初始状态private STATE initialState;// 系统潜在的最后状态private Set<STATE> finalStates;// Event事件 --><初始状态,结束状态> 的映射图private final LoadingCache<EVENT, Map<STATE, STATE>> transitions =CacheBuilder.newBuilder().build(CacheLoader.from((Supplier<Map<STATE, STATE>>) () -> new HashMap()));public StateMachine(STATE initState, Set<STATE> finalStates) {this.initialState = initState;this.finalStates = finalStates;}.../*** 添加状态变化规则*/public void addTransition(STATE from, STATE to, EVENT e) {// 在e对应的event事件内增加新<from, to>状态变化对transitions.getUnchecked(e).put(from, to);}/*** 获取下一状态,根据当前状态和触发event事件*/public STATE getNextState(STATE from, EVENT e)throws InvalidStateTransitionException {// 获取到下一状态STATE target = transitions.getUnchecked(e).get(from);if (target == null) {throw new InvalidStateTransitionException(from, e);}return target;}
}

这个代码也不是绝对的,只要我们能够表达上图中的event和状态间的关系即可,没有强制性的结构定义规则。

类结构定义完毕之后,我们第一步要做的操作就是初始化状态机对象,通过addTransition方法构造出之前设计好的关系结构。相当于一次add操作就是加了一个箭头。那么总共就是4次添加操作。

private void initializeStateMachine() {   stateMachine.addTransition(OzoneProtos.LifeCycleState.ALLOCATED,OzoneProtos.LifeCycleState.CREATING,OzoneProtos.LifeCycleEvent.BEGIN_CREATE);stateMachine.addTransition(OzoneProtos.LifeCycleState.CREATING,OzoneProtos.LifeCycleState.OPEN,OzoneProtos.LifeCycleEvent.COMPLETE_CREATE);stateMachine.addTransition(OzoneProtos.LifeCycleState.OPEN,OzoneProtos.LifeCycleState.CLOSED,OzoneProtos.LifeCycleEvent.CLOSE);stateMachine.addTransition(OzoneProtos.LifeCycleState.OPEN,OzoneProtos.LifeCycleState.DELETING,OzoneProtos.LifeCycleEvent.DELETE);
}

状态机在系统中如何应用


状态机类定义好了,那么我们如何在系统中进行实际应用呢?此时我们只需要明白一点,其它系统代码无须关注对象状态的变换规则,它只负责传入档期状态以及event事件,后面的状态结果交给状态机对象来做。下面是一个简单的例子:

  /*** {@inheritDoc}* Used by client to update container state on SCM.*/@Overridepublic OzoneProtos.LifeCycleState updateContainerState(String containerName,OzoneProtos.LifeCycleEvent event) throws IOException {ContainerInfo containerInfo;lock.lock();try {...containerInfo = ContainerInfo.fromProtobuf(OzoneProtos.SCMContainerInfo.PARSER.parseFrom(containerBytes));OzoneProtos.LifeCycleState newState;try {// 传入当前container状态,和event事件对象,通过状态机获取到下一个新的状态newState = stateMachine.getNextState(containerInfo.getState(), event);} catch (InvalidStateTransitionException ex) {throw new SCMException("Failed to update container state"+ containerName + ", reason : invalid state transition from state: "+ containerInfo.getState() + " upon event: " + event + ".",SCMException.ResultCodes.FAILED_TO_CHANGE_CONTAINER_STATE);}// 设置新状态containerInfo.setState(newState);containerStore.put(dbKey, containerInfo.getProtobuf().toByteArray());return newState;} finally {lock.unlock();}}

其实我们可以看到,这样就可以避免了很多很重的判定逻辑分散在程序各地。

希望通过简单的分析能够给读者朋友带来收获。

状态机在分布式系统中的应用相关推荐

  1. 分布式系统中的一致性协议之两阶段提交协议(2PC)

    分布式系统中的一致性协议之两阶段提交协议(2PC) 两阶段提交协议是很常见的解决分布式事务的方式,他可以保证分布式事务中,要么所有参与的进程都提交事务成功,要么都取消事务,这样做可以在分布式环境中保持 ...

  2. 实战案例丨分布式系统中如何用python实现Paxos

    本文分享自华为云社区<实战分布式系统-python实现Paxos>,原文作者:Leo Xiao . 一致性算法背景:Paxos 一致性算法解决的问题:分布式系统中数据不能存在单个节点(主机 ...

  3. 分布式系统中的幂等性

    分布式系统中的幂等性 分布式系统中的幂等性 1.幂等性介绍 2.幂等性场景 3.crud操作的幂等性分析 4.如何解决幂等性问题 分布式系统中的幂等性 1.幂等性介绍 幂等的概念来自数学,比如对于一元 ...

  4. 面包房算法-时钟和分布式系统中事件的顺序

    http://zh.wikipedia.org/wiki/Lamport面包店算法 类比 Lamport把这个并发控制算法非常直观地类比为顾客去面包店采购.面包店一次只能接待一位顾客的采购.已知有n位 ...

  5. 谈谈分布式系统中的复制

    谈谈分布式系统中的复制 数据极客  2016-04-01 23:16 复制几乎是构成分布式系统,尤其是分布式存储和分布式数据库的关键所在,那么本文就来综合谈论下复制技术. 简单说复制本身可以分为同步复 ...

  6. 分布式系统中节点之间的同步形成区块链

    链客,专为开发者而生,有问必答! 此文章来自链客区块链技术问答社区,未经允许拒绝转载. 分布式系统中节点之间的同步形成区块链 分布式系统由Tanenbaum定义,"分布式系统是一组独立的计算 ...

  7. 工作中感受到的消息中间件在分布式系统中的使用场景

    经历 以前在qunar实习,第一次接触消息中间件,那时候概念还不清楚,朦朦胧胧有个初步认识,现在正式工作了,又一次接触了消息中间件,初步总结几种场景. 场景 1.分布式系统中,不同系统之间传递消息. ...

  8. 状态机在计算机中的应用

    当你参加比赛前,教练会问你状态如何:当你参加考试前,父母会问你状态如何:当你参加演出前,伙伴会问你状态如何.如果状态不好,那么你在这些活动的表现就可能不尽如人意,相反则可能有出人意料的表现. 可见,一 ...

  9. 分布式系统中只有两个难题

    分布式系统抽象 讨论编程语言时,我们使用通用术语并用函数.运算符.类.变量和指针来定义我们的程序.通用的词汇可以帮助我们避免每次都为了描述某些东西而发明新词.我们的定义越精确.越没有歧异,听众也就越容 ...

最新文章

  1. 基于Pytorch的从零开始的目标检测 | 附源码
  2. goland 关闭 自动移除未使用的包  自动添加需要的包
  3. linux+apache+mysql+php
  4. RabbitMQ 关键词解释
  5. v4l2 框架下如何设置分辨率_【微学习】低压计量电表如何设置?(下)
  6. android 列表图片优化经历
  7. php对pdf关键字定位,如何在PDF文件中快速查找关键字
  8. RestTemplate返回List类型,用数组接收
  9. 基于Python2.7的阿里云API调用及运维相关
  10. 洛谷P1074 靶形数独 [搜索]
  11. window开机 关机 记录日志
  12. 宠物商店 - MLDN 李兴华老师
  13. 【论文撰写和程序员常用软件】
  14. Spring学习的书-夏昕(3)
  15. 基础篇——树莓派远程连接工具VNC不显示视频或摄像头画面解决方式
  16. 掌握Android图像显示原理(上)
  17. 奔梦向前-代码实现表白男生女生-2020-06-15
  18. CSS 字体粗细 font-weight属性
  19. Linux下的压缩解压缩工具(转载)
  20. 服务器登录信息记录,服务器记录远程桌面登录的信息

热门文章

  1. java-selenium 实战详解
  2. matlab里的矩阵和opencv里的矩阵的区别,opencv 矩阵相乘, matlab矩阵相乘,以及自己写的矩阵相乘的时间比较...
  3. 小程序request接口的封装
  4. 前端的【趁手兵器】——VSCode
  5. java 统计文章中每个单词出现的次数
  6. MySQL长事务处理办法
  7. 生化危机系列中有实用价值且能最快实现的技术
  8. KRUPS摩卡啤酒回顾
  9. 图像处理笔记(2)---- OpenCV imread函数详解
  10. C++传递参数给Python