暂无图片
暂无图片
暂无图片
暂无图片
暂无图片

looplab/fsm 源码阅读

        github.com/looplab/fsm实现了一个有限状态机,下面研究下它的源码,除了测试文件外,它有下面几个文件:

    errors.go //定义了错误
    fsm.go 定义了状态机的核心逻辑
    event.go 定义了事件结构体
    graphviz_visualizer.go 生成graphviz格式的文件
    mermaid_visualizer.go 生成mermaid格式的文件
    visualizer.go

    总的来说代码分为两部分:1,定义状态机;2,实现状态机的可视化。

    第一部分:状态机的定义

            状态机大体上可以分为两部分:状态和驱动状态变化的事件。首先我们来定义一个门的状态机,它有两个状态open,closed。对应的有两个事件open和close来驱动状态机状态的变化。

        fsm := fsm.NewFSM(
      "closed",
      fsm.Events{
      {Name: "open", Src: []string{"closed", "open"}, Dst: "open"},
      {Name: "close", Src: []string{"open"}, Dst: "closed"},
      },
      fsm.Callbacks{
      //事件之前
      "before_open": func(e *fsm.Event) {
      fmt.Println("before_open")
      e.Async()
      },
      "before_event": func(e *fsm.Event) {
      fmt.Println("before_event")
      e.Async()
      },
      //离开老状态之前
      "leave_closed": func(e *fsm.Event) {
      fmt.Println("leave_closed")
      e.Async()
      },
      "leave_state": func(e *fsm.Event) {
      fmt.Println("leave_state")
      e.Async()
      },
      //进入新状态之前
      "enter_open": func(e *fsm.Event) {
      fmt.Println("enter_open")
      e.Async()
      },
      "enter_state": func(e *fsm.Event) {
      fmt.Println("enter_state")
      e.Async()
      },
      //事件执行之后
      "after_open": func(e *fsm.Event) {
      fmt.Println("after_open")
      e.Async()
      },
      "after_event": func(e *fsm.Event) {
      fmt.Println("after_event")
      e.Async()
      },
      },
      )
      if err := fsm.Event("close"); err != nil {
      fmt.Println(err)
        }
      if err := fsm.Event("open"); err != nil {
      fmt.Println(err)
      }
      fmt.Println(fsm.Current())
      err := fsm.Transition()
      if err != nil {
      fmt.Println(err)
      }
      fmt.Println(fsm.Current())
      graphviz, _ := vfsm.VisualizeWithType(fsm, "graphviz")
      ioutil.WriteFile("fsm.graphviz", []byte(graphviz), fs.ModePerm)
      diagram, _ := vfsm.VisualizeWithType(fsm, "mermaid-state-diagram")
      ioutil.WriteFile("fsm.diagram.md", []byte("```mermaid\n"+diagram+"\n```"), fs.ModePerm)
      flowChart, _ := vfsm.VisualizeWithType(fsm, "mermaid-flow-chart")
      ioutil.WriteFile("fsm.flowChart.md", []byte("```mermaid\n"+flowChart+"\n```"), fs.ModePerm)

      可以看到,定义状态机的时候有三部分组成:状态机的初始状态,状态机的事

      件(包括了多个源事件和一个目的事件),状态机的事件回调。整体来说,回

      调可以分为四组8个回调,按执行顺序依次为:

      1,事件开始之前

      A,before_xxx,特定的状态之前

      B,before_event所有状态之前

      2,离开老状态

      A,leave_xxx 离开特定状态

      B,leave_state 离开所有状态

      3,进入新状态

      A,enter_xxx,进入特定状态

      B,enter_state 进入所有状态

      4,事件执行完毕之后

      A,after_xxx 进入特定状态之后

      B,after_event 进入所有状态

      接着就是两个比较重要的接口:fsm.Event("open")通过传入事件驱动状态的变化,通过传入的事件,从transitions中筛选出对应的transition,初始化当前目标状态的transaction,除了执行transaction本身的交易逻辑外还执行上述8个callback方法,。fsm.Transition()仅仅执行transaction逻辑。下面结合源码具体看看:

      fsm.go

        func NewFSM(initial string, events []EventDesc, callbacks map[string]Callback) *FSM {
        f.transitions[eKey{e.Name, src}] = e.Dst
        f.callbacks[cKey{target, callbackType}] = fn

        NewFSM主要工作是展开我们传入的参数,变成transitions 的map和callbacks 的map,方便后面调用。其中状态机FSM的定义如下:

          type FSM struct {
          current string //当前状态
                transitions map[eKey]string
                // 事件名,事件类型到目标状态映射
          callbacks map[cKey]Callback
                 //回调目标状态,回调类型到回调方法映射
          transition func()
                //调用transition的方法
                transitionerObj transitioner
          stateMu sync.RWMutex
          eventMu sync.Mutex
           }
            type EventDesc struct {
            Name string
            Src []string
            Dst string
              }
                  type Callback func(*Event)
              type Events []EventDesc
              type Callbacks map[string]Callback

              为了线程安全,对状态转换和事件回调两个map都定义了锁。他们的key分别是:

                    type cKey struct {
                target string
                callbackType int
                    }
                    type eKey struct {
                event string
                src string
                    }

                其中transitioner是一个接口

                  type transitioner interface {
                  transition(*FSM) error
                  }

                  默认实现如下,它调用了状态机的transition方法:

                    func (t transitionerStruct) transition(f *FSM) error {
                    f.transition()
                     }

                    状态机实现的函数接口有:

                            func (f *FSM) AvailableTransitions() []string
                            func (f *FSM) Can(event string) bool
                      func (f *FSM) Cannot(event string) bool
                      func (f *FSM) Current() string
                      func (f *FSM) Event(event string, args ...interface{}) error
                      func (f *FSM) Is(state string) bool
                      func (f *FSM) Metadata(key string) (interface{}, bool)
                      func (f *FSM) SetMetadata(key string, dataValue interface{})
                      func (f *FSM) SetState(state string)
                      func (f *FSM) Transition() error

                      其中

                        func (f *FSM) Can(event string) bool
                          _, ok := f.transitions[eKey{event, f.current}]
                          return ok && (f.transition == nil)
                          //执行事件扭转
                          func (f *FSM) Event(event string, args ...interface{}) error{
                              e := &Event{f, event, f.current, dst, nil, args, falsefalse}
                          err := f.beforeEventCallbacks(e)
                             f.transition = func() {
                               f.enterStateCallbacks(e)
                               f.afterEventCallbacks(e)
                              }
                             f.leaveStateCallbacks(e)
                             err = f.doTransition()
                          }


                          func (f *FSM) enterStateCallbacks(e *Event){
                          if fn, ok := f.callbacks[cKey{f.current, callbackEnterState}]; ok {
                                  fn(e)
                          }
                          if fn, ok := f.callbacks[cKey{"", callbackEnterState}]; ok {
                          fn(e)
                          }
                          }
                            //执行
                            func (f *FSM) Transition() error{
                                 return f.doTransition()
                            }
                            func (f *FSM) doTransition() error {
                            return f.transitionerObj.transition(f)
                            }

                            接着我们看下event.go里面事件的定义:

                              type Event struct {
                              FSM *FSM
                              Event string
                              Src string
                              Dst string
                              Err error
                              Args []interface{}
                              canceled bool
                              async bool
                                }

                              有两个函数

                                func (e *Event) Cancel(err ...error) 
                                func (e *Event) Async()

                                第二部分:状态机的可视化

                                支持两种格式,三种方式的可视化graphviz_visualizer.go实现了Graphviz格式展示状态机

                                  writeHeaderLine(&buf)
                                  writeTransitions(&buf, fsm.current, sortedEKeys, fsm.transitions)
                                  writeStates(&buf, sortedStateKeys)
                                  writeFooter(&buf)

                                  mermaid_visualizer.go实现MermaidDiagramType 格式化,支持两种格式:

                                    VisualizeForMermaidWithGraphType
                                    case FlowChart:
                                    return visualizeForMermaidAsFlowChart(fsm), nil
                                    case StateDiagram:
                                    return visualizeForMermaidAsStateDiagram(fsm), nil

                                    visualizer.go定义了基础接口和公用函数的实现

                                      func Visualize(fsm *FSMstring{
                                      VisualizeWithType
                                      case GRAPHVIZ:
                                      return Visualize(fsm), nil
                                      case MERMAID:
                                      return VisualizeForMermaidWithGraphType(fsm, StateDiagram)
                                      case MermaidStateDiagram:
                                      return VisualizeForMermaidWithGraphType(fsm, StateDiagram)
                                      case MermaidFlowChart:
                                      return VisualizeForMermaidWithGraphType(fsm, FlowChart)
                                      }

                                      在前文的例子中我们生成了上述三种格式对应的文件如何可视化呢,对于graphviz,可以转化成图片格式

                                        dot -T png fsm.graphviz -o fsm.dot.png

                                        对于Mermaid格式,可是安装vscode插件Markdown Preview Mermaid Support,然后通过markdown代码段的方式可视化,打开后点击cmd shift v可以看到下面的效果:

                                        至此源码分析完毕。


                                        文章转载自golang算法架构leetcode技术php,如果涉嫌侵权,请发送邮件至:contact@modb.pro进行举报,并提供相关证据,一经查实,墨天轮将立刻删除相关内容。

                                        评论