stateMachine

状态机

为了让复杂逻辑更容易维护,代码更容易清晰,明了。YARN 使用了事件触发。事件触发有两个核心概念:中央异步调度器和状态机。 前文介绍了中央异步处理器,本文将介绍YARN 另一核心概念-状态机。

1. 简介

状态机是为了处理单一对象的输入,输出,状态转换逻辑的抽象。在YARN中状态机都跟对象强绑定,代码实现表现为每个对象中都有一个状态机属性。

2. 功能

状态机在功能上能够抽象为:某个对应在某个状态下,因为某个输出,触发某个某个操作,进而变成另一个状态。

3. 设计和实现

3.1 概要

每个对象都有一个属性stateMachine 处理状态转换逻辑

private final StateMachine<RMAppState, RMAppEventType, RMAppEvent>
                                                               stateMachine;

状态机将对象的输入,状态转换,输出过程梳理为如下五个过程: 1. 在prevState状态,接收到事件event,变成postState状态 2. 在prevState状态,接收到事件event,触发hook操作,变成postState状态 3. 在prevState状态,接收到事件event1或event2或event3,变成postState状态 4. 在prevState状态,接收到事件event1或event2或event3,触发hook操作,变成postState状态 5. 在prevState状态,接收到事件event,触发hook操作,变成postState1或postState2或postState3状态 通过StateMachineFactory 对象实现上面对应过程。

3.2 缓存

StateMachineFactory 其内部维护两个缓存: 1. Map>> stateMachineTable 2. final TransitionsListNode transitionsListNode

stateMachineTable 结构为:

Map<STATE, Map<EVENTTYPE,
            Transition<OPERAND, STATE, EVENTTYPE, EVENT>>> stateMachineTable

是两层map ,存放状态和事件类型对应的转移操作(很大程度上是为了检索更加方便),表示某个状态碰见某个事件类型,触发什么转换操作。 其存储结构可以通过下图来表示

transitionsListNode 是一个状态转移操作对象(ApplicableSingleOrMultipleTransition)列表,如下图所示 其内部只有一个属性 ApplicableTransition(该接口只有一个方法apply),唯一的实现是ApplicableSingleOrMultipleTransition ApplicableSingleOrMultipleTransition 有三个属性: preState 触发前的状态。 eventType 事件类型。 Transition 执行触发操作。

stateMachineTable 需要在 transitionsListNode支持下才能构建。 构建逻辑是:先将所有的状态流转流程和触发操作写入 transitionsListNode,然后遍历transitionsListNode,来构建 stateMachineTable

3.3 状态机构建基本流程

3.3.1 初始化

初始化构造函数1,defaultInitialState 表示这个对象在初始化之后,在状态机中的初始化状态。

  public StateMachineFactory(STATE defaultInitialState) {
  this.transitionsListNode = null;
  this.defaultInitialState = defaultInitialState;
  this.optimized = false;
  this.stateMachineTable = null;
}

3.3.2 添加状态流转过程

上面提到,yarn 抽象来五个状态流转流程,每个流转流程都对应一个addTransition 方法(注意返回值是StateMachineFactory)

StateMachineFactory
             <OPERAND, STATE, EVENTTYPE, EVENT>
          addTransition()

最终每个方法执行都是状态机都构造函数2,添加的状态流转流程会放入内部缓存transitionsListNode ,

private StateMachineFactory
    (StateMachineFactory<OPERAND, STATE, EVENTTYPE, EVENT> that,
     ApplicableTransition<OPERAND, STATE, EVENTTYPE, EVENT> t) {
  this.defaultInitialState = that.defaultInitialState;
  this.transitionsListNode
      = new TransitionsListNode(t, that.transitionsListNode);
  this.optimized = false;
  this.stateMachineTable = null;
}

3.3.3 构建

待所有都状态流转都添加进状态机,状态机就需要开始构建内部缓存。调用的是构造函数3,构造函数 传入的 optimized 参数为true。 该构造函数通过调用makeStateMachineTable() 方法, 该方法将缓存在 transitionsListNode 的流转流程,转换并存到为两层map stateMachineTable

  private StateMachineFactory
      (StateMachineFactory<OPERAND, STATE, EVENTTYPE, EVENT> that,
       boolean optimized) {
    this.defaultInitialState = that.defaultInitialState;
    this.transitionsListNode = that.transitionsListNode;
    this.optimized = optimized;
    if (optimized) {
      makeStateMachineTable();
    } else {
      stateMachineTable = null;
    }
  }

3.4 状态机执行流程

以RMAppImpl 为例,描述状态机是如何使用的

3.4.1 构建状态机

a. 调用StateMachineFactory 第一个构造函数,初始化状态机 b. 调用StateMachineFactory 第二个构造函数,添加状态流转流程 如:添加RMAppNewlySavingTransition 操作

  .addTransition(RMAppState.NEW, RMAppState.NEW_SAVING,
    RMAppEventType.START, new RMAppNewlySavingTransition())

第二个构造函数其实就干了一件事情:构建 transitionsListNode 链

this.transitionsListNode
        = new TransitionsListNode(t, that.transitionsListNode);

c. 调用StateMachineFactory 第三个构造函数,完成状态机构建,并将状态机赋值给变量stateMachine

.installTopology()
``
`
第三个构造函数其实最终调用的就是 makeStateMachineTable 方法  
该方法内容包括:  
1. 初始化一个空的 两层map stateMachineTable   
2. 遍历 transitionsListNode 链  
3. 从 transitionsListNode 链中取出 ApplicableTransition<OPERAND, STATE, EVENTTYPE, EVENT>  对象,并执行其 apply() 方法  
apply 方法是实际的构建 两层 map stateMachineTable 的过程
```java
@Override
public void apply
        (StateMachineFactory<OPERAND, STATE, EVENTTYPE, EVENT> subject) {
    Map<EVENTTYPE, Transition<OPERAND, STATE, EVENTTYPE, EVENT>> transitionMap
            = subject.stateMachineTable.get(preState);
    if (transitionMap == null) {
        // I use HashMap here because I would expect most EVENTTYPE's to not
        //  apply out of a particular state, so FSM sizes would be
        //  quadratic if I use EnumMap's here as I do at the top level.
        transitionMap = new HashMap<EVENTTYPE,
                Transition<OPERAND, STATE, EVENTTYPE, EVENT>>();
        subject.stateMachineTable.put(preState, transitionMap);
    }
    transitionMap.put(eventType, transition);
}

d. 将构建到状态机赋值给变量 最终通过make方法将状态机赋值给 stateMachine变量 ,注意make方法返回到是一个InternalStateMachine 对象

final StateMachine<RMAppState, RMAppEventType, RMAppEvent> this.stateMachine = stateMachineFactory.make(this);

InternalStateMachine 对象关键是实现了StateMachine ,并内部存了两个属性: 状态机实例和状态机当前状态

private final OPERAND operand;
private STATE currentState;

3.4.2 生成事件,触发状态机

以RMAppEventType.START 事件为例 当用户通过Hadoop jar 提交一个mr ,在rm 收到请求之后,rm 的 RMAppManager 服务会创建一个 RMAppEventType.START 类型的RMAppEvent事件 ,并通过下面的方式提交rm的中央调度器中。

this.rmContext.getDispatcher().getEventHandler()
  .handle(new RMAppEvent(applicationId, RMAppEventType.START));

3.4.3 状态机内部执行堆栈

a. 接收事件 因为RMAppImpl 本身也是一个事件处理器,而且注册到了RM 到中央异步处理器中,所有可以通过如下方式,感知到有RMAppEvent 事件产生

  public void handle(RMAppEvent event) {
    this.writeLock.lock();
    try {
      this.stateMachine.doTransition(event.getType(), event);
    } catch (InvalidStateTransitonException e) {
      LOG.error("Can't handle this event at current state", e);
    } finally {
    this.writeLock.unlock();
  }
}

b. 触发流转操作 执行堆栈: stateMachine.doTransition() -> InternalStateMachine.doTransition() -> StateMachineFactory.this.doTransition() -> SingleInternalArc/MultipleInternalArc.doTransition() -> SingleArcTransition/MultipleArcTransition.transition() -> RMAppTransition.transition() -> RMAppNewlySavingTransition.transition() 在状态机内部,通过如下逻辑找到事件+状态 需要触发的操作 RMAppNewlySavingTransition.transition()

private STATE doTransition
         (OPERAND operand, STATE oldState, EVENTTYPE eventType, EVENT event)
    throws InvalidStateTransitonException {
  Map<EVENTTYPE, Transition<OPERAND, STATE, EVENTTYPE, EVENT>> transitionMap
    = stateMachineTable.get(oldState);
  if (transitionMap != null) {
    Transition<OPERAND, STATE, EVENTTYPE, EVENT> transition
        = transitionMap.get(eventType);
    if (transition != null) {
      return transition.doTransition(operand, oldState, event, eventType);
    }
  }
}

4. YANR 状态转换图

上面详细说明了状态机的实现机理,相信在YARN代码中遇到状态机的代码view的思路就更清晰了。就可以不用纠结状态机的内部逻辑,而直接从状态机构建来了解对象的流转过程,甚至可以通过下图,直接了解resourceManager和NodeManger的各 个对象的状态流转过程。 下面给出通过代码转换来的YARN各个对象的状态转换图

Last updated

Was this helpful?