成人免费xxxxx在线视频软件_久久精品久久久_亚洲国产精品久久久_天天色天天色_亚洲人成一区_欧美一级欧美三级在线观看

狀態機在馬蜂窩機票訂單交易系統中的應用與優化實踐

開發 開發工具
在設計交易系統時,穩定性、可擴展性、可維護性都是我們需要關注的重點。本文將對如何通過狀態機在交易系統中的應用解決上述問題做出一些探討。

 [[262292]]

在設計交易系統時,穩定性、可擴展性、可維護性都是我們需要關注的重點。本文將對如何通過狀態機在交易系統中的應用解決上述問題做出一些探討。

關于馬蜂窩機票訂單交易系統

交易系統往往存在訂單維度多、狀態多、交易鏈路長、流程復雜等特點。以馬蜂窩大交通業務中的機票交易為例,用戶提交的一個訂單除了機票信息之外可能還包含很多信息,比如保險或者其他附加產品。其中保險又分為很多類型,如航意險、航延險、組合險等。

從用戶的維度看,一個訂單是由購買的主產品機票和附加產品共同構成,支付的時候是作為一個整體去支付,而如果想要退票、退保也是可以部分操作的;從供應商的維度看,一個訂單中的每個產品背后都有獨立的供應商,機票有機票的供應商,保險有保險的供應商,每個供應商的訂單都需要分開出票、獨立結算。

用戶的購買支付流程、供應商的出票出保流程,構成一個有機的整體穿插在機票交易系統中,密不可分。

狀態機在機票交易系統中的應用與優化

有限狀態機的概念

有限狀態機(以下簡稱狀態機)是一種用于對事物或者對象行為進行建模的工具。

狀態機將復雜的邏輯簡化為有限個穩定狀態,構建在這些狀態之間的轉移和動作等行為的數學模型,在穩定狀態中判斷事件。

對狀態機輸入一個事件,狀態機會根據當前狀態和觸發的事件唯一確定一個狀態遷移。

 

圖1:FSM工作原理

業務系統的本質就是描述真實的世界,因此幾乎所有的業務系統中都會有狀態機的影子。訂單交易流程更是天然適合狀態機模型的應用。

以用戶支付流程為例,如果不使用狀態機,在接收到支付成功回調時則需要執行一系列動作:查詢支付流水號、記錄支付時間、修改主訂單狀態為已支付、通知供應商去出票、記錄通知出票時間、修改機票子訂單狀態為出票中…… 邏輯非常繁瑣,而且代碼耦合嚴重。

為了使交易系統的訂單狀態按照設計流程正確向下流轉,比如當前用戶已支付,不允許再支付;當前訂單已經關單,不能再通知出票等等,我們通過應用狀態機的方式來優化機票交易系統,將所有的狀態、事件、動作都抽離出來,對復雜的狀態遷移邏輯進行統一管理,來取代冗長的 if else 判斷,使機票交易系統中的復雜問題得以解耦,變得直觀、方便操作,使系統更加易于維護和管理。

狀態機設計

在數據庫設計層面,我們將整個訂單整體作為一個主訂單,把供應商的訂單作為子訂單。假設一個用戶同時購買了機票和保險,因為機票、保險對應的是不同的供應商,也就是 1 個主訂單 order 對應 2 個子訂單 sub_order。其中主訂單 order 記錄用戶的信息(UID、聯系方式、訂單總價格等),子訂單 sub_order 記錄產品類型、供應商訂單號、結算價格等。

同時,我們把正向出票、逆向退票改簽分開,抽成不同的子系統。這樣每個子系統都是完全獨立的,有利于系統的維護和拓展。

對于機票正向子系統而言,有兩套狀態機:主訂單狀態機負責管理 order 的狀態,包括創單成功、支付成功、交易成功、訂單關閉等;子訂單狀態機負責管理 sub_order 的狀態,維護預訂成功到出票的流程。同樣,對于逆向退票和改簽子系統,也會有各自的狀態機。

 

圖2:機票主訂單狀態機狀態轉移示例

框架選型

目前業界常用的狀態機引擎框架主要有 Spring Statemachine、Stateless4j、Squirrel-Foundation 等。經過結合實際業務進行橫向對比后,最終我們決定使用 Squirrel-Foundation,主要是因為:

  1. 代碼量適中,擴展和維護相對而言比較容易;
  2. StateMachine 輕量,實例創建開銷小;
  3. 切入點豐富,支持狀態進入、狀態完成、異常等節點的監聽,使轉換過程留有足夠的切入點;
  4. 支持使用注解定義狀態轉移,使用方便;
  5. 從設計上不支持單例復用,只能隨用隨 New,因此狀態機的本身的生命流管理很清晰,不會因為狀態機單例復用的問題造成麻煩。

MSM 的設計與實現

結合大交通業務邏輯,我們在 Squirrel-Foundation 的基礎之上進行了 Action 概念的抽取和二次封裝,將狀態遷移、異步消息糅合到一起,封裝成為 MSM 框架 (MFW State Machine),用來實現業務訂單狀態定義、事件定義和狀態機定義,并用注解的形式來描述狀態遷移。

我們認為一次狀態遷移必然會伴隨著異步消息,因此把一個流程中必須要成功的數據庫操作放到一個事務中,把允許失敗重試并且對實時度要求不高的操作放到異步消息消費的流程中。

以機票訂單支付成功為例,機票訂單支付成功時,會涉及修改訂單狀態為已支付、更新支付流水號等,這些是在一個事務中;而通知供應商出票,則是放在異步消息消費中處理。異步消息的實現使用的是 RocketMQ,主要考慮到 RocketMQ 支持二階段提交,消息可靠性有保證,支持重試,支持多個 Consumer 組。

以下具體說明:

1. 對每個狀態遷移需要執行的動作,都會抽取出一個Action 類,并且繼承 AbstractAction,支持多個不同的狀態遷移執行相同的動作。這里主要取決于 public List matchConditions() 的實現,因此只需要 matchConditions 返回多個初始狀態-事件的匹配條件鍵值對就可以了。每個 Action 都有一個對應的繼承 MFWContext 類的上下文類,用于在 process saveDB 等方法中的通信。

2. 注冊所有的 Action,添加每個狀態遷移執行完成或者執行失敗的監聽。

3. 由于依賴 RocketMQ 異步消息,所以需要一個 Spring Bean 去繼承 BaseMessageSender,這個類會生成異步消息提供者。如果要使用二階段提交,則需要一個類繼承 BaseMsgTransactionListener,這里可以參考機票的 OrderChangeMessageSender 和 OrderChangeMsgTransactionListener。

4. 最后,實現一個事件觸發器類。在這個類里面包含一個 Apply 方法,傳入訂單 PO 對象、事件、對應的上下文,每次執行都實例化出一個狀態機實例,并初始化當前狀態,并調用 Fire 方法。

5. 實例化一個狀態機對象,設置當前狀態為數據庫對應的狀態,調用 Fire 方法之后,最終會執行到 OrderStateMachine 類里面用注解描述的 callMethod 方法。我們配置的是 callMethod = "action",它就會反射執行當前類的 Action 方法。

Action 方法我們的實現是通過 super.action(from, to, event, context),就會執行 MFWStateMachine 的 Action 方法,先去根據當前狀態和事件獲取對應的Action,這里使用到了「工廠模式」,然后執行 Process 方法。如果成功,會執行在 MFWStateMachine 類初始化的 TransitionCompleteListener,執行該 Action的 afterProcess 方法來修改數據庫記錄以及發送消息;如果失敗,會執行TransitionExceptionListener,執行該 Action 的onException 方法來進行相應處理。

綜上,MSM 可以根據 Action 類的聲明和配置,來動態生成出 Squirrel-Foundation 的狀態機定義,而不需要由使用方再去定義一次,使 MSM 的使用更方便。

 

圖3: UML

趟過的坑

1. 事務不生效

最初我們使用 Spring 注解方式進行事務管理,即在 Action 類的數據庫操作方法上加 @Transactional 注解,卻發現在實踐中不起作用。經過排查后發現, Spring 的事務注解是靠 AOP 切面實現的。在對象內部的方法中調用該對象其他使用 AOP 注解的方法,被調用方法的 AOP 注解會失效。因為同一個類的內部代碼調用中,不會走代理類。后來我們通過手動開啟事務的方式來解決此問題。

2. 匹配 Action

最初我們匹配 Action 有兩種方式:精準匹配及非精準匹配。精準匹配是指只有當某個狀態遷移的初始狀態和觸發的事件一致時,才能匹配到 Action;非精準匹配是指只要觸發的事件一致,就可以匹配到 Action。后來我們發現非精準匹配在某些情形下會出現問題,于是統一改成了多條件精準匹配。即在執行狀態機觸發時執行的 Action 方法時,去精準匹配 Action,多個狀態遷移執行的方法可以匹配到同一個 Action,這樣能夠復用 Action 代碼而不會出問題。

3. 異步消息一致性

有一些情況是絕不能出現的,比如修改數據庫沒成功即發出了消息;或是修改數據庫成功了,而發送消息失敗;或是在提交數據庫事務之前,消息已經發送成功了。解決這個問題我們用到了 RocketMQ 的事務消息功能,它支持二階段提交,會先發送一條預處理消息,然后回調執行本地事務,最終提交或者回滾,幫助保證修改數據庫的信息和發送異步消息的一致。

4. 同一條訂單數據并發執行不同事件

在某些情況下,同一條訂單數據可能會在同一時間(毫秒級)同時觸發不同的事件。如機票主訂單在待支付狀態下,可以接收支付中心的回調,觸發支付成功事件;也可以由用戶點擊取消訂單,或者超時未支付定時任務來觸發關單事件。如果不做任何控制的話,一個訂單將可能出現既支付成功又會被取消。

我們用數據庫樂觀鎖來規避這個問題:在執行修改數據庫的事務時,update 訂單的語句帶有原狀態的條件判斷,通過判斷更新行數是否為 1,來決定是否拋出異常,即生成這樣的 SQL 語句:update order where order_id = ‘1234' and order_status = ‘待支付'。

這樣的話,如果兩個事件同時觸發同時執行,誰先把事務提交成功,誰就能執行成功;事務提交較晚的事件會因為更新行數為 0 而執行失敗,最終回滾事務,就仿佛無事發生過一樣。

使用悲觀鎖也可以解決這個問題,這種方式是誰先爭搶到鎖誰就可以成功執行。但考慮到可能會有腳本對數據庫批量修改,悲觀鎖存在死鎖的潛在問題,我們最終還是采用了樂觀鎖的方式。

總結

MSM 狀態機的定義和聲明在 Squirrel-Foundation 的基礎之上,抽取出 Action 概念,并對 Action 類配置起始狀態、目標狀態、觸發的事件、上下文定義等,使 MSM 可以根據 Action 類的聲明和配置,來動態生成出 Squirrel-Foundation 的狀態機定義,而不需要使用方再去定義一次,操作更簡單,維護起來也更容易。

通過使用狀態機,機票訂單交易系統的流程復雜性問題迎刃而解,系統在穩定性、可擴展性、可維護性等方面也得到了顯著的改善和提升。

狀態機的使用場景不僅僅局限于訂單交易系統,其他一些涉及到狀態變更的復雜流程的系統也同樣適用。希望通過本文的介紹,能使有狀態機了解和使用需求的讀者朋友有所收獲。

本文作者:董天,馬蜂窩大交通研發團隊機票交易系統研發工程師。

【本文是51CTO專欄作者馬蜂窩技術的原創文章,作者微信公眾號馬蜂窩技術(ID:mfwtech)】

 

戳這里,看該作者更多好文

責任編輯:武曉燕 來源: 51CTO專欄
相關推薦

2020-03-22 15:49:27

Kafka馬蜂窩大數據平臺

2020-01-03 09:53:36

Kafka集群優化

2019-06-11 12:19:10

ABTest分流系統

2019-03-25 15:14:19

Flutter馬蜂窩開發

2019-02-18 15:23:21

馬蜂窩MESLambda

2019-02-27 15:24:54

馬蜂窩游搶單系統

2019-06-11 11:18:40

容災緩存設計

2022-06-20 09:00:00

深度學習人工智能研究

2019-04-26 15:16:02

馬蜂窩火車票系統

2019-02-19 15:20:12

消息總線架構異步

2020-02-21 16:20:37

系統驅動項目管理

2023-03-06 07:35:30

狀態機工具訂單狀態

2019-12-17 14:59:27

數據中臺數據倉庫馬蜂窩

2018-10-29 12:27:20

2019-03-29 08:21:51

馬蜂窩Golang并發代理

2024-12-06 11:58:16

2018-10-26 16:00:39

程序員爬蟲馬蜂窩

2025-04-02 00:00:03

2024-04-02 08:45:08

ChatGPTAI會議人工智能

2013-09-03 09:57:43

JavaScript有限狀態機
點贊
收藏

51CTO技術棧公眾號

主站蜘蛛池模板: 久久69精品久久久久久久电影好 | 99爱视频 | 国产欧美日韩精品一区 | 91免费在线看 | 免费看a| 免费特级黄毛片 | 久草新在线 | 99pao成人国产永久免费视频 | 超碰国产在线 | 国产精品99久久久久久www | 亚洲欧洲日韩精品 中文字幕 | 亚洲 欧美 另类 综合 偷拍 | 免费h在线| 欧美一区二区三区视频在线 | 国产精品日韩欧美一区二区三区 | 日本视频在线播放 | 日日骚视频| 精品国产乱码久久久久久88av | 日本三级电影免费观看 | 91精品国产乱码麻豆白嫩 | 日本电影一区二区 | 99亚洲精品 | 亚洲毛片在线观看 | 粉嫩一区二区三区国产精品 | 亚洲一区久久 | 国产成人免费在线观看 | 毛片一区二区三区 | 久久99精品久久久久久狂牛 | 亚洲福利网| 精品乱人伦一区二区三区 | 国产视频一区二区 | 欧美中文视频 | 国产精品1区 | 色婷婷久久久久swag精品 | 国产在线1| 欧美色性 | 日本黄色片免费在线观看 | 国产精品视频一区二区三 | 久久一级| 国产免费拔擦拔擦8x高清 | av一区二区三区四区 |