偷偷摘套内射激情视频,久久精品99国产国产精,中文字幕无线乱码人妻,中文在线中文a,性爽19p

狀態(tài)機在馬蜂窩機票訂單交易系統(tǒng)中的應用與優(yōu)化實踐

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

 [[262292]]

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

關(guān)于馬蜂窩機票訂單交易系統(tǒng)

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

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

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

狀態(tài)機在機票交易系統(tǒng)中的應用與優(yōu)化

有限狀態(tài)機的概念

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

狀態(tài)機將復雜的邏輯簡化為有限個穩(wěn)定狀態(tài),構(gòu)建在這些狀態(tài)之間的轉(zhuǎn)移和動作等行為的數(shù)學模型,在穩(wěn)定狀態(tài)中判斷事件。

對狀態(tài)機輸入一個事件,狀態(tài)機會根據(jù)當前狀態(tài)和觸發(fā)的事件唯一確定一個狀態(tài)遷移。

 

圖1:FSM工作原理

業(yè)務(wù)系統(tǒng)的本質(zhì)就是描述真實的世界,因此幾乎所有的業(yè)務(wù)系統(tǒng)中都會有狀態(tài)機的影子。訂單交易流程更是天然適合狀態(tài)機模型的應用。

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

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

狀態(tài)機設(shè)計

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

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

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

 

圖2:機票主訂單狀態(tài)機狀態(tài)轉(zhuǎn)移示例

框架選型

目前業(yè)界常用的狀態(tài)機引擎框架主要有 Spring Statemachine、Stateless4j、Squirrel-Foundation 等。經(jīng)過結(jié)合實際業(yè)務(wù)進行橫向?qū)Ρ群?,最終我們決定使用 Squirrel-Foundation,主要是因為:

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

MSM 的設(shè)計與實現(xiàn)

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

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

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

以下具體說明:

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

2. 注冊所有的 Action,添加每個狀態(tài)遷移執(zhí)行完成或者執(zhí)行失敗的監(jiān)聽。

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

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

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

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

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

 

圖3: UML

趟過的坑

1. 事務(wù)不生效

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

2. 匹配 Action

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

3. 異步消息一致性

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

4. 同一條訂單數(shù)據(jù)并發(fā)執(zhí)行不同事件

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

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

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

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

總結(jié)

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

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

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

本文作者:董天,馬蜂窩大交通研發(fā)團隊機票交易系統(tǒng)研發(fā)工程師。

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

 

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

責任編輯:武曉燕 來源: 51CTO專欄
相關(guān)推薦

2020-03-22 15:49:27

Kafka馬蜂窩大數(shù)據(jù)平臺

2020-01-03 09:53:36

Kafka集群優(yōu)化

2019-06-11 12:19:10

ABTest分流系統(tǒng)

2019-03-25 15:14:19

Flutter馬蜂窩開發(fā)

2019-02-18 15:23:21

馬蜂窩MESLambda

2019-02-27 15:24:54

馬蜂窩游搶單系統(tǒng)

2019-06-11 11:18:40

容災緩存設(shè)計

2022-06-20 09:00:00

深度學習人工智能研究

2019-04-26 15:16:02

馬蜂窩火車票系統(tǒng)

2019-02-19 15:20:12

消息總線架構(gòu)異步

2020-02-21 16:20:37

系統(tǒng)驅(qū)動項目管理

2023-03-06 07:35:30

狀態(tài)機工具訂單狀態(tài)

2019-12-17 14:59:27

數(shù)據(jù)中臺數(shù)據(jù)倉庫馬蜂窩

2018-10-29 12:27:20

2019-03-29 08:21:51

馬蜂窩Golang并發(fā)代理

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有限狀態(tài)機
點贊
收藏

51CTO技術(shù)棧公眾號