源码 状态机_【YARN源码阅读】基础模块3:状态机(StateMachine)

“状态机四要素:现态,条件,动作,次态

状态机,通常被用来管理一个实体的生命周期。通过它,可以使复杂的状态转化问题模式化,达到代码高内聚的效果。事实上,状态机也确实是一种设计模式。

状态机有四个要素:

  • 现态:实体的当前状态

  • 次态:实体的下个状态

  • 条件:引发实体状态转移的条件

  • 动作:条件发生时,伴随的动作

示意图如下:

f27c0a9a660dd682a1236bcf6af03d97.png

01

目标代码

状态机的代码路径如下:

./hadoop-release-2.7.0/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-common/src/main/java/org/apache/hadoop/yarn/state

这个模块的代码是在YARN项目下,所以设计目的也是只针对YARN。

该路径,包含下列代码:

state $ tree.|-- Graph.java|-- InvalidStateTransitionException.java|-- MultipleArcTransition.java|-- package-info.java|-- SingleArcTransition.java|-- StateMachine.java|-- StateMachineFactory.java|-- VisualizeStateMachine.java

先对其中的工具代码做简要介绍:

Graph/VisualizeStateMachine: 这两个文件的目的是为了将状态机中的复杂关系可视化。

InvalidStateTransitionException: 该模块独有的异常。

接下来看剩下的这些代码的类图关系。

02

类图关系

先看SingleArcTransition和MultipleArcTransition。

708b4f392d6ec8b62c8cb1ed962e4c0b.png

这两个都是接口类,其实并没有什么类图关系。放在一起主要是想指出两点。

第一,为什么要从概念上区分两者,差别在哪里。对于SingleArc来说,它的现态和次态是单一确定的;但是对于MultipleArc来说,它的次态则可以有多个。transition的方法的返回值其实也暗示了这点。前者返回void,因为状态关系明确;后者返回STATE,需要指明究竟是多个次态中的哪一个。真正能够说明两者区别的代码在StateMachineFactory中,它也是本文的唯一关键类,我们放在关键类分析中细讲。

第二,前文提到,状态机的四要素是现态,次态,条件,动作。而实际上,我们transition方法,其实只包含了两项,即条件(对应event)和动作(对应方法本身),第一个参数operand是状态机关联的实体。那么为什么没有包含现态和次态两项呢?一个揣测是,接口设计者的本意是通过operand来访问两个状态。

但是,StateMachineFactory的实现者似乎不喜欢这个不透明的接口,所以之后分析该类代码时,我们会发现其内部,又定义了一个Transition接口,及其实现   SingleInternalArc和MultipleInternalArc,两者分别对原始接口进行了封装。

再来看StateMachine,它也是一个接口类,类图如下:

ff97c1217fe49263046ee5730aae2e5b.png

最后是StateMachineFactory,是一个实现类。其内部又定义了一些接口及实现,类图如下:

8a5faac8c135d678d3719fef83b50f42.png

03

关键类分析

状态机模块只有一个关键类,即StateMachineFactory。从类图可以看出它的复杂性。现在一点点剖析。

首先,以Factory结尾的类都是工厂类,遵循工厂模式,其目的就是将构建目标的过程(StateMachineFactory)和目标(StateMachine)本身解耦。

这个工厂的关键目标有两个:

  • 添加转移过程(addTransition)

  • 基于已经添加的转移过程,构建状态机(make)

那么先看addTransition,这个方法有很多签名:

public StateMachineFactory addTransition(STATE preState, STATE postState, EVENTTYPE eventType); # 单个事件类型,不包含动作public StateMachineFactory addTransition(STATE preState, STATE postState, Set eventTypes); 多个事件类型,不包含动作public StateMachineFactory addTransition(STATE preState, STATE postState, Set eventTypes, SingleArcTransition hook); 多个事件类型,包含SingleArcpublic StateMachineFactoryaddTransition(STATE preState, STATE postState,EVENTTYPE eventType,SingleArcTransition hook); 单个事件类型,包含SingleArcpublic StateMachineFactoryaddTransition(STATE preState, Set postStates,EVENTTYPE eventType,MultipleArcTransition hook); 单个事件类型,包含MultipleArc,多个次态

真正起作用的是最后两个,而他们本质上是调用了private标识的构造函数:

private StateMachineFactory      (StateMachineFactory that,       ApplicableTransition t) {    this.defaultInitialState = that.defaultInitialState;    this.transitionsListNode         = new TransitionsListNode(t, that.transitionsListNode); # 追加transitionsListNode链表    this.optimized = false;    this.stateMachineTable = null; # 清空状态表  }

再看make:

public StateMachinemake(OPERAND operand) {    return new InternalStateMachine(operand, defaultInitialState);  }# 构造函数  InternalStateMachine(OPERAND operand, STATE initialState) {      this.operand = operand;      this.currentState = initialState;      if (!optimized) {        maybeMakeStateMachineTable(); # 构建状态表      }    }

构建的核心逻辑在MakeStateMachineTable中:

private void makeStateMachineTable() {    Stack> stack =      new Stack>();    MapMap      prototype = new HashMapMap    prototype.put(defaultInitialState, null);    // I use EnumMap here because it'll be faster and denser.  I would    //  expect most of the states to have at least one transition.    stateMachineTable       = new EnumMapMap                           Transition>>(prototype);    for (TransitionsListNode cursor = transitionsListNode;         cursor != null;         cursor = cursor.next) { # 遍历applicableTransition链表并压栈      stack.push(cursor.transition);    }    while (!stack.isEmpty()) {      stack.pop().apply(this); # 取出元素,并apply。apply会把元素放入statemachinetable中    }  }

最后,StateMachineFactory还承担了本不该自己承担的重任:doTransition。是的,InternalStateMachine的doTransition方法竟然代理到了工厂类,有些匪夷所思。

04

总结

YARN中的许多功能模块都有较复杂的状态定义,状态机在其中起到了关键作用。这部分代码虽然实现方式给人一种绕来绕去的感觉,但整体逻辑还是比较易懂的。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值