您好,登錄后才能下訂單哦!
這篇文章主要介紹“Go語言狀態機如何實現”,在日常操作中,相信很多人在Go語言狀態機如何實現問題上存在疑惑,小編查閱了各式資料,整理出簡單好用的操作方法,希望對大家解答”Go語言狀態機如何實現”的疑惑有所幫助!接下來,請跟著小編一起來學習吧!
1. 定義
有限狀態機(Finite-state machine, FSM),簡稱狀態機,是表示有限個狀態以及在這些狀態之間的轉移和動作等行為的數學模型。
2. 組成要素
現態(src state):事務當前所處的狀態。
事件(event):事件就是執行某個操作的觸發條件,當一個事件被滿足,將會觸發一個動作,或者執行一次狀態的遷移。
動作(action):事件滿足后執行的動作。動作執行完畢后,可以遷移到新的狀態,也可以仍舊保持原狀態。動作不是必需的,當事件滿足后,也可以不執行任何動作,直接遷移到新狀態。
次態(dst state):事件滿足后要遷往的新狀態。“次態”是相對于“現態”而言的,“次態”一旦被激活,就轉變成新的“現態”了。
狀態流轉(transition):事物從現態轉為次態的整個過程。
3. 優點
代碼抽象:將業務流程進行抽象和結構化,將復雜的狀態轉移圖,分割成相鄰狀態的最小單元。這樣相當于搭建樂高積木,在這套機制上可以組合成復雜的狀態轉移圖,同時隱藏了系統的復雜度。
簡化流程:業務rd只需要關注當前操作的業務邏輯(狀態流轉過程中的業務回調函數),極大的解耦了狀態和業務。
易擴展:在新增狀態或事件時,無需修改原有的狀態流轉邏輯,直接建立新的狀態轉移鏈路即可。
業務建模:通過最小粒度的相鄰狀態拼接,最終組成了業務的整體graph。
假設我們有要實現一個訂單下單功能,上圖是訂單狀態的流轉圖,方框為訂單的狀態,箭頭旁的文字為事件。
package database import "fmt" // DB 模擬數據庫對象 type DB struct { } // Transaction 模擬事務 func (db *DB) Transaction(fun func() error) error { fmt.Println("事務執行開始。") err := fun() fmt.Println("事務執行結束。") return err } // Order 訂單 type Order struct { ID int64 // 主鍵ID State int // 狀態 } type OrderList []*Order // 查詢所有訂單 func ListAllOrder() (OrderList, error) { orderList := OrderList{ &Order{1, 0}, &Order{2, 1}, &Order{2, 2}, } return orderList, nil } // UpdateOrderState 更新訂單狀態 func UpdateOrderState(curOrder *Order, srcState int, dstState int) error { if curOrder.State == srcState { curOrder.State = dstState } fmt.Printf("更新id為 %v 的訂單狀態,從現態[%v]到次態[%v]\n", curOrder.ID, srcState, dstState) return nil }
用來模擬數據庫的操作,如數據庫事務、查詢所有訂單、更新訂單狀態等。
package fsm import ( "fmt" "reflect" "zuzhiang/database" ) // FSMState 狀態機的狀態類型 type FSMState int // FSMEvent 狀態機的事件類型 type FSMEvent string // FSMTransitionMap 狀態機的狀態轉移圖類型,現態和事件一旦確定,次態和動作就唯一確定 type FSMTransitionMap map[FSMState]map[FSMEvent]FSMDstStateAndAction // FSMTransitionFunc 狀態機的狀態轉移函數類型 type FSMTransitionFunc func(params map[string]interface{}, srcState FSMState, dstState FSMState) error // FSMDstStateAndAction 狀態機的次態和動作 type FSMDstStateAndAction struct { DstState FSMState // 次態 Action FSMAction // 動作 } // FSMAction 狀態機的動作 type FSMAction interface { Before(bizParams map[string]interface{}) error // 狀態轉移前執行 Execute(bizParams map[string]interface{}, tx *database.DB) error // 狀態轉移中執行 After(bizParams map[string]interface{}) error // 狀態轉移后執行 } // FSM 狀態機,元素均為不可導出 type FSM struct { transitionMap FSMTransitionMap // 狀態轉移圖 transitionFunc FSMTransitionFunc // 狀態轉移函數 } // CreateNewFSM 創建一個新的狀態機 func CreateNewFSM(transitionFunc FSMTransitionFunc) *FSM { return &FSM{ transitionMap: make(FSMTransitionMap), transitionFunc: transitionFunc, } } // SetTransitionMap 設置狀態機的狀態轉移圖 func (fsm *FSM) SetTransitionMap(srcState FSMState, event FSMEvent, dstState FSMState, action FSMAction) { if int(srcState) < 0 || len(event) <= 0 || int(dstState) < 0 { panic("現態|事件|次態非法。") return } transitionMap := fsm.transitionMap if transitionMap == nil { transitionMap = make(FSMTransitionMap) } if _, ok := transitionMap[srcState]; !ok { transitionMap[srcState] = make(map[FSMEvent]FSMDstStateAndAction) } if _, ok := transitionMap[srcState][event]; !ok { dstStateAndAction := FSMDstStateAndAction{ DstState: dstState, Action: action, } transitionMap[srcState][event] = dstStateAndAction } else { fmt.Printf("現態[%v]+事件[%v]+次態[%v]已定義過,請勿重復定義。\n", srcState, event, dstState) return } fsm.transitionMap = transitionMap } // Push 狀態機的狀態遷移 func (fsm *FSM) Push(tx *database.DB, params map[string]interface{}, currentState FSMState, event FSMEvent) error { // 根據現態和事件從狀態轉移圖獲取次態和動作 transitionMap := fsm.transitionMap events, eventExist := transitionMap[currentState] if !eventExist { return fmt.Errorf("現態[%v]未配置遷移事件", currentState) } dstStateAndAction, ok := events[event] if !ok { return fmt.Errorf("現態[%v]+遷移事件[%v]未配置次態", currentState, event) } dstState := dstStateAndAction.DstState action := dstStateAndAction.Action // 執行before方法 if action != nil { fsmActionName := reflect.ValueOf(action).String() fmt.Printf("現態[%v]+遷移事件[%v]->次態[%v], [%v].before\n", currentState, event, dstState, fsmActionName) if err := action.Before(params); err != nil { return fmt.Errorf("現態[%v]+遷移事件[%v]->次態[%v]失敗, [%v].before, err: %v", currentState, event, dstState, fsmActionName, err) } } // 事務執行execute方法和transitionFunc if tx == nil { tx = new(database.DB) } transactionErr := tx.Transaction(func() error { fsmActionName := reflect.ValueOf(action).String() fmt.Printf("現態[%v]+遷移事件[%v]->次態[%v], [%v].execute\n", currentState, event, dstState, fsmActionName) if action != nil { if err := action.Execute(params, tx); err != nil { return fmt.Errorf("狀態轉移執行出錯:%v", err) } } fmt.Printf("現態[%v]+遷移事件[%v]->次態[%v], transitionFunc\n", currentState, event, dstState) if err := fsm.transitionFunc(params, currentState, dstState); err != nil { return fmt.Errorf("執行狀態轉移函數出錯: %v", err) } return nil }) if transactionErr != nil { return transactionErr } // 執行after方法 if action != nil { fsmActionName := reflect.ValueOf(action).String() fmt.Printf("現態[%v]+遷移事件[%v]->次態[%v], [%v].after\n", currentState, event, dstState, fsmActionName) if err := action.After(params); err != nil { return fmt.Errorf("現態[%v]+遷移事件[%v]->次態[%v]失敗, [%v].before, err: %v", currentState, event, dstState, fsmActionName, err) } } return nil }
狀態機包含的元素有兩個:狀態轉移圖和狀態轉移函數,為了防止包外直接調用,這兩個元素都設為了不可導出的。狀態轉移圖說明了狀態機的狀態流轉情況,狀態轉移函數定義了在狀態轉移的過程中需要做的事情,在創建狀態時指定,如更新數據庫實體(order)的狀態。
而狀態轉移圖又包含現態、事件、次態和動作,一旦現態和事件確定,那么狀態流轉的唯一次態和動作就隨之確定。
狀態機的動作又包含三個:Before、Execute和After。Before操作在是事務前執行,由于此時沒有翻狀態,所以該步可能會被重復執行。Execute操作是和狀態轉移函數在同一事務中執行的,同時成功或同時失敗。After操作是在事務后執行,因為在執行前狀態已經翻轉,所以最多會執行一次,在業務上允許執行失敗或未執行。
狀態機的主要方法有兩個,SetTransitionMap方法用來設置狀態機的狀態轉移圖,Push方法用來根據現態和事件推動狀態機進行狀態翻轉。Push方法中會先執行Before動作,再在同一事務中執行Execute動作和狀態轉移函數,最后執行After動作。在執行Push方法的時候會將params參數傳遞給狀態轉移函數。
(1) order
package order import ( "fmt" "zuzhiang/database" "zuzhiang/fsm" ) var ( // 狀態 StateOrderInit = fsm.FSMState(0) // 初始狀態 StateOrderToBePaid = fsm.FSMState(1) // 待支付 StateOrderToBeDelivered = fsm.FSMState(2) // 待發貨 StateOrderCancel = fsm.FSMState(3) // 訂單取消 StateOrderToBeReceived = fsm.FSMState(4) // 待收貨 StateOrderDone = fsm.FSMState(5) // 訂單完成 // 事件 EventOrderPlace = fsm.FSMEvent("EventOrderPlace") // 下單 EventOrderPay = fsm.FSMEvent("EventOrderPay") // 支付 EventOrderPayTimeout = fsm.FSMEvent("EventOrderPayTimeout") // 支付超時 EventOrderDeliver = fsm.FSMEvent("EventOrderDeliver") // 發貨 EventOrderReceive = fsm.FSMEvent("EventOrderReceive") // 收貨 ) var orderFSM *fsm.FSM // orderTransitionFunc 訂單狀態轉移函數 func orderTransitionFunc(params map[string]interface{}, srcState fsm.FSMState, dstState fsm.FSMState) error { // 從params中解析order參數 key, ok := params["order"] if !ok { return fmt.Errorf("params[\"order\"]不存在。") } curOrder := key.(*database.Order) fmt.Printf("order.ID: %v, order.State: %v\n", curOrder.ID, curOrder.State) // 訂單狀態轉移 if err := database.UpdateOrderState(curOrder, int(srcState), int(dstState)); err != nil { return err } return nil } // Init 狀態機的狀態轉移圖初始化 func Init() { orderFSM = fsm.CreateNewFSM(orderTransitionFunc) orderFSM.SetTransitionMap(StateOrderInit, EventOrderPlace, StateOrderToBePaid, PlaceAction{}) // 初始化+下單 -> 待支付 orderFSM.SetTransitionMap(StateOrderToBePaid, EventOrderPay, StateOrderToBeDelivered, PayAction{}) // 待支付+支付 -> 待發貨 orderFSM.SetTransitionMap(StateOrderToBePaid, EventOrderPayTimeout, StateOrderCancel, nil) // 待支付+支付超時 -> 訂單取消 orderFSM.SetTransitionMap(StateOrderToBeDelivered, EventOrderDeliver, StateOrderToBeReceived, DeliverAction{}) // 待發貨+發貨 -> 待收貨 orderFSM.SetTransitionMap(StateOrderToBeReceived, EventOrderReceive, StateOrderDone, ReceiveAction{}) // 待收貨+收貨 -> 訂單完成 } // ExecOrderTask 執行訂單任務,推動狀態轉移 func ExecOrderTask(params map[string]interface{}) error { // 從params中解析order參數 key, ok := params["order"] if !ok { return fmt.Errorf("params[\"order\"]不存在。") } curOrder := key.(*database.Order) // 初始化+下單 -> 待支付 if curOrder.State == int(StateOrderInit) { if err := orderFSM.Push(nil, params, StateOrderInit, EventOrderPlace); err != nil { return err } } // 待支付+支付 -> 待發貨 if curOrder.State == int(StateOrderToBePaid) { if err := orderFSM.Push(nil, params, StateOrderToBePaid, EventOrderPay); err != nil { return err } } // 待支付+支付超時 -> 訂單取消 if curOrder.State == int(StateOrderToBePaid) { if err := orderFSM.Push(nil, params, StateOrderToBePaid, EventOrderPayTimeout); err != nil { return err } } // 待發貨+發貨 -> 待收貨 if curOrder.State == int(StateOrderToBeDelivered) { if err := orderFSM.Push(nil, params, StateOrderToBeDelivered, EventOrderDeliver); err != nil { return err } } // 待收貨+收貨 -> 訂單完成 if curOrder.State == int(StateOrderToBeReceived) { if err := orderFSM.Push(nil, params, StateOrderToBeReceived, EventOrderReceive); err != nil { return err } } return nil }
order包中做的事情主要有:
定義訂單狀態機的狀態和事件;
創建一個狀態機,并設置狀態轉移函數和狀態轉移圖;
執行訂單任務,推動狀態轉移。
(2) order_action_place
package order import ( "fmt" "zuzhiang/database" ) type PlaceAction struct { } // Before 事務前執行,業務上允許多次操作 func (receiver PlaceAction) Before(bizParams map[string]interface{}) error { fmt.Println("執行下單的Before方法。") return nil } // Execute 事務中執行,與狀態轉移在同一事務中 func (receiver PlaceAction) Execute(bizParams map[string]interface{}, tx *database.DB) error { fmt.Println("執行下單的Execute方法。") return nil } // After 事務后執行,業務上允許執行失敗或未執行 func (receiver PlaceAction) After(bizParams map[string]interface{}) error { fmt.Println("執行下單的After方法。") return nil }
(2) ~ (5)是訂單不同動作的聲明和實現。
(3) order_action_pay
package order import ( "fmt" "zuzhiang/database" ) type PayAction struct { } // Before 事務前執行,業務上允許多次操作 func (receiver PayAction) Before(bizParams map[string]interface{}) error { fmt.Println("執行支付的Before方法。") return nil } // Execute 事務中執行,與狀態轉移在同一事務中 func (receiver PayAction) Execute(bizParams map[string]interface{}, tx *database.DB) error { fmt.Println("執行支付的Execute方法。") return nil } // After 事務后執行,業務上允許執行失敗或未執行 func (receiver PayAction) After(bizParams map[string]interface{}) error { fmt.Println("執行支付的After方法。") return nil }
(4) order_action_deliver
package order import ( "fmt" "zuzhiang/database" ) type DeliverAction struct { } // Before 事務前執行,業務上允許多次操作 func (receiver DeliverAction) Before(bizParams map[string]interface{}) error { fmt.Println("執行發貨的Before方法。") return nil } // Execute 事務中執行,與狀態轉移在同一事務中 func (receiver DeliverAction) Execute(bizParams map[string]interface{}, tx *database.DB) error { fmt.Println("執行發貨的Execute方法。") return nil } // After 事務后執行,業務上允許執行失敗或未執行 func (receiver DeliverAction) After(bizParams map[string]interface{}) error { fmt.Println("執行發貨的After方法。") return nil }
(5) order_action_receive
package order import ( "fmt" "zuzhiang/database" ) type ReceiveAction struct { } // Before 事務前執行,業務上允許多次操作 func (receiver ReceiveAction) Before(bizParams map[string]interface{}) error { fmt.Println("執行收貨的Before方法。") return nil } // Execute 事務中執行,與狀態轉移在同一事務中 func (receiver ReceiveAction) Execute(bizParams map[string]interface{}, tx *database.DB) error { fmt.Println("執行收貨的Execute方法。") return nil } // After 事務后執行,業務上允許執行失敗或未執行 func (receiver ReceiveAction) After(bizParams map[string]interface{}) error { fmt.Println("執行收貨的After方法。") return nil }
package main import ( "fmt" "zuzhiang/database" "zuzhiang/order" ) func main() { order.Init() orderList, dbErr := database.ListAllOrder() if dbErr != nil { return } for _, curOrder := range orderList { params := make(map[string]interface{}) params["order"] = curOrder if err := order.ExecOrderTask(params); err != nil { fmt.Printf("執行訂單任務出錯:%v\n", err) } fmt.Println("\n\n") } }
最后在main包里先初始化一個訂單狀態機,查詢所有訂單,并使用狀態機執行訂單任務,推動訂單狀態轉移。注意多個訂單可以用同一個狀態機來進行狀態的遷移。
到此,關于“Go語言狀態機如何實現”的學習就結束了,希望能夠解決大家的疑惑。理論與實踐的搭配能更好的幫助大家學習,快去試試吧!若想繼續學習更多相關知識,請繼續關注億速云網站,小編會繼續努力為大家帶來更多實用的文章!
免責聲明:本站發布的內容(圖片、視頻和文字)以原創、轉載和分享為主,文章觀點不代表本網站立場,如果涉及侵權請聯系站長郵箱:is@yisu.com進行舉報,并提供相關證據,一經查實,將立刻刪除涉嫌侵權內容。