區塊鏈技術資源分享
追尋中本聰先生的腳步
?

狀態機在分布式系統中的應用_csdn(Android路上的人)

在大型的分布式系統中,經常會涉及到狀態的改變,這里的狀態變化可以分很多種,最極端的情況是,任何狀態之間都可以互相切換。

這種狀態之間的切換,轉變,更加官方一點的稱為叫狀態機。這個詞可能很多人會感到比較陌生,英文就是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.
   */
  @Override
  public 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();
    }
  }

其實我們可以看到,這樣就可以避免了很多很重的判定邏輯分散在程序各地。

希望通過簡單的分析能夠給讀者朋友帶來收獲。

分享到:更多 ()
區塊鏈神吐槽

來評論吐槽 搶沙發

評論前必須登錄!

 

區塊鏈資源分享

韭菜的自我進化
新疆25选7开奖结果是多少钱