交易中臺之訂單系統架構淺析

導讀:

百度交易中臺作爲集團移動生態戰略的基礎設施,面向收銀交易與清分結算場景,爲賦能業務提供高效交易生態搭建。目前支持百度體系內多個產品線,主要包含:小程序,地圖打車,百家號,招財貓,好看視頻等。本文主要從業務模型與架構設計兩個方面介紹訂單系統的構建過程。

一、訂單系統應具備怎樣的能力?

訂單打通用戶、商家、商品、庫存、售後等關鍵業務,是驅動交易全流程運轉的核心。而訂單系統承上啓下,作爲入口,涵蓋了訂單流程管理、庫存與營銷管理、算價引擎、履約子流程、售後以及退款信息管理等。

訂單系統具備的能力可以按照下面三個角度進行切入拆解:

二、交易中臺如何提供服務?

交易中臺基於現有的業務進行了抽象和歸類,從接入主要分成如下三個類型:

上圖中介紹了通用業務和自營業務關鍵環節中的對比。可以看到:

三、訂單的生命週期以及流程

在常見的電商環節中,訂單從產生之後,主要包括訂單確認、支付、發貨、成功、取消、退款等,這些狀態構成了一個有限狀態機。

這些狀態主要通過兩個動作進行串聯,即:訂單的正向流程及逆向流程,正向流程是指用戶購買產品或者服務的支付行爲管理。逆向流程則是指用戶發起售後造成的退款、退貨行爲管理。

3.1 正向支付流程

正向支付流程,由用戶發起,代表用戶向商家發起一筆交易,交易的流程入下圖所示:

3.2 逆向退款流程

在實際的業務場景中,逆向退款主要是指商家進行退款的流程。通常可以在電商場景中的 7 天無理由退款、退貨、用戶售後流程中見到。

中臺經過抽象業務流程之後,梳理了一套退款退貨流程,如上圖所示,退款退貨發起之後,會進入商家審覈的環節,商家確認通過之後,會進行用戶處理。如果有物流環節的話,這時候會處理商家退貨發貨等。

業務流程就介紹到這裏,接下來主要做系統架構方面的介紹。

四、架構淺談

技術本身的目標是爲業務服務,貼合業務的技術架構本身是最經濟的選擇。訂單系統也是一樣,架構隨着業務的發展進新了逐步的優化和擴展。

在業務初期,架構如下圖所示:

業務初期規模較小,功能也比較單一,只需要具備簡單的支付、退款能力。所有功能都集中在一個系統,這樣做的好處是簡單快捷,容易部署,測試、開發效率高,是適合業務初期發展的架構。訂單系統初期也爲百度內部的火車票、小度商城、小程序的業務提供訂單管理的能力。

隨着業務不斷擴張,虛擬商品的購買,退款已經不能滿足業務,需要擴展支持帶有物流商品訂單,並且在支付方式部分,需要擴展支持各類購買入口和場景,比如聚合掃碼支付、小額免密支付、週期代扣。隨着業務擴展,後續又引入了諸如直播帶貨、拍賣、閃電購、訂單評價、紅包搶購,資產充值等更加豐富的場景。並且在功能擴展之外,整體交易中臺還必須引入符合央行的監管規範改造,爲了訂單安全對接反作弊入口等諸多非功能方面的擴展。

爲了支持業務的多方向擴展,原來的單體架構在功能需求方面會遇到了擴展難的問題,同時在性能方面,也逐漸無法滿足吞吐量,響應時間以及擴展性等要求。

對於性能方面的擴展,需要將系統從單體改造爲分佈式的架構,這一部分的改造方案較爲成熟,採用集團內的分佈式數據庫、緩存、以及商業平臺近年來提供的雲原生部署架構可以較爲快速的進行提升。唯一的難點就在於訂單分佈式數據庫的改造,由於業務初期已經充分考慮了訂單的擴展進行持久層結構設計,這部分擴展也不難。

對於業務方面的擴展則是重頭戲,訂單系統構建了一套指令編排架構,通過不同指令調用不同的系統,然後抽象出模板,然後通過不同模板指令支撐不同業務場景。並且通過緩存,異步,降級等方式來提升性能。

分佈式技術改造方案非常成熟,不在此進行贅述。接下來主要介紹一下基於指令進行設計方案,以及基於該方案專門設計的性能升級改造。

4.1 指令編排架構

不同的產品形態、交易類型產生的流程各式各樣,爲了滿足這種不同場景中的業務需求,訂單系統通過抽象了指令編排的設計,來實現業務流程的管理,從而使系統更具擴展性。

指令可以簡單理解爲相對獨立的操作單元,比如常見的功能點都可以拆分爲指令集,比如支付指令、用戶指令等。優點在於代碼的改動較小,遵循開閉原則。編排的方式類似於模板方法,不同的指令類似搭積木一樣的進行疊加,即可實現不同業務的流程。

實際的實現中,訂單系統將業務訴求拆解成不同的指令集,並且提供不同指令操作。

通過指令的組合形成規則,通過組合不同規則抽象出具體的模板,進行實例化從而產生具體的接入模型以供不同業務接入。

通過不同模板指令,可以快速支撐不同業務場景。通過對複雜指令集的優化,還可以使訂單系統的吞吐量,拓展性,穩定性都得到很大提升。

下面列舉一個訂單拆分業務的案例進行說明。

用戶支付完訂單後,需要獲取訂單的支付信息,包括支付流水號、支付時間等。支付完訂單接着就是等商家發貨,但在發貨過程中,根據平臺業務模式的不同,可能會涉及到訂單的拆分(如果是充值、消費類業務不存在發貨情況)。

訂單拆分原因一般分兩種:

(1)電商場景的合併支付: 用戶商品來自不同商家,需要進行拆單進行分賬;

(2)問答、諮詢類場景: 用戶支付時並不知道哪個平臺或者答者會接單、回答。只有用戶提問最終完成服務之後才能確定具體的商家,該場景也需要後續拆單。

這種業務可以抽象爲拆單指令進行實現。

創建拆單指令可以進行拆解,拆解爲兩種指令:拆單類型 + 拆單策略。根據業務需求通過指令的組合,抽象出規則並生成二種類型的模板(購物車拆單模板、諮詢問答類拆單模板)並且實例化出接入模型對外輸出。

當有購物車需求的業務可以跟進接入類型選擇購物車拆單模板如:度小店。

當有諮詢問答或者支付後拆單需求的業務可以使用諮詢問答拆單模板如:醫療,百度地圖,盎司手機充值等。

4.2 架構性能優化

上一章講解了指令編排在生產環境中承擔業務場景,接下來會講解指令編排架構遇到的問題,以及進行優化。

上圖是通過指令編排架構生成的一個通用下單模板,功能沒有問題,但是在較高流量的場景下,會遇到性能方面的挑戰。

可以看出的問題有:

針對問題,我們逐個進行擊破。

4.2.1 長鏈路串行問題

首先是調用鏈過長問題做了以下優化。

通過以上二點完成調用鏈過長,穩定性無法保障的問題。

4.2.2 數據庫重複獲取壓力

針對每個指令是獨立執行單元要重複獲取數據的問題使用以下解決方案。

同一線程重複查詢數據做到線程級別傳遞,使用 ThreadLocal 方式進行線程之間的數據傳遞。

ThreadLocal 提供了線程本地變量,它可以保證訪問到的變量屬於當前線程,每個線程都保存有一個變量副本,每個線程的變量都不同。ThreadLocal 相當於提供了一種線程隔離,將變量與線程相綁定。

因通過線程池把非關鍵路徑的指令異步化後,發現異步化的指令無法使用 ThreadLocal 進行數據傳遞,從而引入全鏈路追蹤組件 TransmittableThreadLocal 進行異步線程數據的傳遞。

TransmittableThreadLocal 繼承 InheritableThreadLocal,使用方式也類似。相比 InheritableThreadLocal,添加了:

(1)copy 方法用於定製 任務提交給線程池時 的 ThreadLocal 值傳遞到 任務執行時 的拷貝行爲,缺省傳遞的是引用。注意:如果跨線程傳遞了對象引用因爲不再有線程封閉,與 InheritableThreadLocal.childValue 一樣,使用者 / 業務邏輯要注意傳遞對象的線程安全。

(2)protected 的 beforeExecute/afterExecute 方法執行任務 (Runnable/Callable) 的前 / 後的生命週期回調。

4.2.3 數據庫性能提升

數據庫受限於物理服務器的 CPU、內存、存儲、連接數等資源,在處理上會遇到性能瓶頸,以及在主從同步存在延遲情況,爲了下單的達到 2 萬 QPS 並且主從無延遲就需要進行性能提升的優化。

首先針對不同業務需要進行數據的隔離以及拆分。需要跟進業務把不同的業務數據進行數據隔離,垂直拆分到不同的庫和機器,從而分別提升不同業務的數據庫性能和容量。(如:訂單,退款,售後,客訴,商品,庫存等)

某些業務雖然庫垂直拆分了,但是單表數據增長太快,當單表數據量太大,會極大的影響 sql 的執行性能,這時 sql 會跑的很慢。這時就需要針對單表進行水平切分來減少單表的數據量。(如:訂單相關表,退款相關表,CPS 記錄表)

訂單表進行水平切分,首先需要確定分表字段。

消費者用戶查詢訂單最頻繁的場景,是通過用戶 id 以及訂單號這兩種類型進行查詢,所以訂單主表的設計需要兼容這兩種查詢。

在數據模型的設計上,由於數據庫分表字段只能有一個,所以這裏採用計算規則將訂單號和用戶 id 進行關聯,即,讓一個用戶所有的訂單都存儲在一張單表之中,具體手段就是通過用戶 id 的一種規則作爲分表字段(shardingKey),訂單 id 生成規則和分表字段做關聯,具體就不進行展開說明。

這樣不論通過用戶 id 還是訂單號查詢都能取到分表字段,從而定位到具體的庫和表。

因將整個訂單庫所有表按照統一規則進行切分,分表規則一致,保證按照同一用戶或者訂單都能在一個庫,從而可以使用數據庫事務。

針對數據庫查詢禁止不帶分表規則信息的維度的查詢,避免造成輪詢數據庫表造成慢查詢情況。

對於查詢用戶 id 以及訂單號之外的查詢場景,分爲兩種實現:對於高實時性的查詢,會建立額外的數據庫索引表進行存儲,比如手機號查詢、外部訂單號查詢等。對於低實時性要求的查詢,比如商家端查詢訂單數據,此時藉助全文檢索的外部數據存儲(比如 ElasticSearch 等數據存儲)實現查詢,我們會通過數據庫 binlog 同步工具,將訂單信息同步到存儲之中來提供查詢。

另外,一定要保證數據查詢要存在索引,保證數據庫可控,能從長遠保證庫的擴展性和容量的提升。

五、總結

本文重點在介紹如何通過架構、技術實現等手段來搭建一個可靠、完善的訂單系統。實際生產中,應該抓住業務上的關鍵問題,在滿足業務的前提下,對流程、需求做合理的減法,以降低整體架構的複雜度。另外,應該合理利用開源項目和第三方平臺服務滿足系統需求,在技術方案和開發成本之間做到較好的折衷。

作者:西西

來源:百度 Geek 說

本文由 Readfog 進行 AMP 轉碼,版權歸原作者所有。
來源https://mp.weixin.qq.com/s/5yQAwX98RAfVfkzdFptcVw