電商交易場景狀態機方案探索及應用
背景
目前閒魚行業產品有回收、寄賣、驗貨寶等,這些產品在基礎交易模式上引入附加玩法規則,其狀態機相比於普通交易模型更加靈活複雜。基礎交易模型訂單狀態只包含:創建訂單 -> 付款 -> 發貨 -> 確認收貨 。以回收舉例,在中臺基礎交易模型之上,又附加了諸多行業業務狀態,如服務商收貨、質檢、用戶確認質檢等。這些狀態,是通過用戶或服務商等多種角色推進的,而業務狀態的維護也需要閒魚自己來負責。
存在問題
目前,在狀態履約的代碼邏輯實現上,狀態履約方法內使用 IF-ELSE 的結構來判斷不同履約動作。在不同的分支裏,根據推進節點的不同,存在不同的參數校驗、業務執行等等操作。這種原始的實現方式,隨着業務的不斷髮展,逐步暴露出以下問題:
-
- 代碼接口複雜:目前回收業務推進某一個單一狀態的代碼就已經達到上百行,如果把整個狀態拓撲的所有履約邏輯都寫到一起的話,則整個履約方法將達到上千行,代碼內聚高、可讀性差。
-
- 模板式代碼冗餘、無複用:如前置校驗、後置消息等。
-
- 無整體拓撲視圖:回收場景的這一套代碼完成後,之後的開發、服務商、測試都不能感知整體的一個拓撲邏輯。現在我們只能使用另外準備的拓撲圖去作爲解釋文檔,但代碼上是沒有能力直接把整個拓撲提取出來的。
-
- 可擴展性差:如果要增加狀態或者刪除狀態,或者是增加一條履約的邊。那麼開發人員都需要去改 if-else。並且開發改起來的時候必須非常的小心謹慎。比如說要加一個狀態,那首先需要整體增加一個 else 代碼塊。另外,在每一個之前 IF-ELSE 分支裏面又需要加上判斷邏輯,單獨處理新加的狀態是否可以做遷移等等,所以擴展性是極差的。
此外,橫向來看,在行業交易的不同產品下,還存在許多共性需求:
-
• 狀態拓撲查詢:在服務商接入、測試迴歸等場景,都需要查詢當前業務的狀態拓撲,以知道當前訂單允許推進到哪些狀態。目前只能依靠另外編寫對接文檔的方式進行溝通。代碼沒有直接提取狀態的能力,有可能導致了對接文檔的滯後性。
-
• 狀態履約執行:在實際線上履約、對接聯調、代碼開發、測試迴歸場景中,都需要執行狀態的履約。另外,在不同的場景中,又希望有單獨的定製功能,比如:在開發、測試過程中,只能推進測試訂單,不能推進實際訂單。
-
• 履約記錄復現:對於一筆訂單的狀態推進記錄可以復現,以幫助技術、運營、客服定位問題。
-
• 履約日常監控:對不同的業務,可以統一提供履約結果的監控,業務也可能根據自身需要單獨配置監控和報警。
解決方案
業界狀態機方案調研
業界幾款流行的開源狀態機框架 (如 spring-statemachine、 squirrel、smart-engine),均適用於典型的通過狀態機管理系統狀態的場景,他們的共性特點是:
-
- 通過狀態機的定義,表達整個系統狀態、控制系統的複雜流轉。
-
- 提供 xml、或鏈式調用等方式定義狀態機,解決複雜狀態機定義可讀性差的問題;
-
- 每個狀態機需要維護一個實例 (對象);
-
- 狀態機對象一般是一個單例的、共享的變量,供整個系統訪問。狀態變換時需要加鎖控制併發。
這幾款狀態機框架通常受管理的對象需要連續的、較長時間的進行跟蹤管理,並伴隨其完整的生命週期。不適用於訂單履約處理,原因是:訂單履約方法一般是無狀態的,內存不需要對每個處理的訂單生成狀態機實例。多筆訂單對應多個履約請求、多個線程併發調用履約方法。我們的需求是做一個輕量級的、僅僅用來支持行業交易履約的場景, 開發同學通過簡單的 api 就可以定義好拓撲、實現最基礎的一兩個接口就可以直接運行,監控、日誌能力甚至開箱可用。
問題抽象
針對上節最後提出的問題,我們用抽象的思維,思考在狀態流程履約中,哪些是不變的點、哪些是變化的點。
可以看到,不變的操作有:
-
• 狀態校驗
-
• 參數校驗
-
• 履約執行
-
• 後置邏輯
這幾個操作,都是每個狀態的履約中,可以抽象的操作步驟,是不變的點。每個步驟中的實現細節,是根據不同業務變化的點。那麼,我們將不變的流程模板化、將變化的邏輯接口化。
狀態流程處理
通過一個 Executor 模板化的執行每個狀態履約動作的步驟,如前置判斷 (validate)、執行狀態動作 (action)、後置動作 (postAction)。對於不同的履約執行動作,抽象出 PerformProcessor 接口,交給業務具體實現。在某個履約流程執行時,Executor 會執行對應的 PerformProcessor。
拓撲抽象
對於拓撲定義以及狀態流處理定義,進行以下抽象:
狀態流程處理器
下面我們來看下狀態流程處理器 PerformProcessor。它是一個接口,
PerformProcessor 中定義了以下幾個方法
-
• init: 初始化流程處理上下文
-
• validate:校驗入參
-
• action:執行流程轉換動作
-
• postAction:執行後置邏輯
-
• finalize:執行收尾工作 (本方法一定會被執行,無論其他方法是否拋異常)
如上圖,Executor 執行器中,會模板化的調用這些方法。
拓撲定義
我們期望定義一個簡明易用的 API,來將狀態拓撲以及履約處理器串聯在一起,完成整個狀態機的定義,示意如下:
整體框架結構
框架包含三層:核心層、膠水層、業務層。核心層提供純粹的狀態機定義、拓撲構造、執行引擎等能力,設計的薄一些,控制架構防腐化;膠水層引入中臺訂單查詢、更新等能力,相對厚一些,簡化業務適配成本、優化業務流程。業務層則基於通用的框架接口,面向業務應用,提供表達能力。
框架核心層
聚焦於三個能力:狀態拓撲定義、拓撲查詢、狀態推進。
狀態拓撲定義對應 FlowBuilder。拓撲查詢和狀態推進對應核心層的 FlowManager,他會查詢 FlowDefination(拓撲定義封裝類) 和 調用 FlowExecutor 執行 PerformProcessor。
膠水層
在狀態機核心層和業務交互層之間,我們抽取了一層膠水層。目的有兩個
-
- 抽取膠水層,可以讓狀態機核心層更純粹,只關心狀態履約的定義、流轉邏輯,不耦合業務依賴。核心層將相關的信息定義爲泛型,也是這個道理。因爲入參、出參、履約上下文不影響狀態機的流轉邏輯,所以核心使用泛型定義。具體類型由上層實現。另外,膠水層中也可以做一些業務相關的通用動作,如記錄狀態轉換日誌等。
-
- 可擴展:該層目前使用 TC 的 BizOrder 信息,供閒魚訂單業務使用。但如果需要擴展,可以另行封裝其他膠水層,不影響核心層我們看到膠水層聲明瞭上下文泛型爲 BizOrderContext, 該對象中包含 TC 的 BizOrderDO、PayOrder 等信息。由於目前閒魚訂單都是依賴中臺訂單,所以目前回收履約、驗貨寶履約等都直接使用 BizOrder 膠水層即可。但如果某個業務實現需要其他領域的 DO,則可以水平擴展膠水層。
業務層
最上層是業務交互層,在不同的業務域內,提供不同的履約功能,比如回收、驗貨寶都會單獨封裝對應的履約服務。主要能力:
-
1. 對外提供業務履約、測試工具、機器人聯調等能力,可分別做權限校驗等邏輯
-
2. 對內基於膠水層,進行拓撲查詢、狀態推進等動作
配套設施
-
• 訂單推單工具:面向日常開發和測試,業務層基於狀態機框架,封裝了推單工具。針對一筆訂單,可自動獲取當前業務狀態、可履約動作、自定義入參。
-
• 統一日誌 & 監控:基於框架收口的履約入口,可以提供跨業務統一的日誌格式,進而搭建統一的、開箱可用的履約日誌查詢和監控。
總結
通過抽象訂單狀態履約的通用問題,提出一套輕量狀態機解決方案。解決了業務靈活多變帶來的諸多技術痛點:1. 通過狀態機履約處理器的封裝,隔離了每個履約動作的業務邏輯, 解決了接口代碼複雜、可讀性差的問題;2. 業務可將通用的業務處理邏輯、校驗邏輯等封裝在通用的處理器中,使得相同邏輯可複用,解決了代碼冗餘、難複用的問題;3. 通過拓撲的抽象,天然的可以獲取到業務的整體狀態拓撲,對於聯調、業務對接等都提供了便利;4. 如遇需求需要調整狀態拓撲,只需修改拓撲定義,即可靈活刪除或者新增狀態拓撲關係。
展望
閒魚行業交易團隊,在閒魚回收、寄賣、驗貨寶的交易履約底層,已全量採用本狀態機引擎,實現了訂單狀態的統一履約控制、履約日誌回溯、監控能力。而後續基於本框架,履約邏輯自動迴歸、履約異常定位可視化等應用場景都將持續構建。
本文由 Readfog 進行 AMP 轉碼,版權歸原作者所有。
來源:https://mp.weixin.qq.com/s/MEt5T3-WdD5gel1XMzbHbw