導讀:百度交易中臺做爲集團移動生態戰略的基礎設施,面向收銀交易與清分結算場景,爲賦能業務提供高效交易生態搭建。目前支持百度體系內多個產品線,主要包含:小程序,地圖打車,百家號,招財貓,好看視頻等。本文主要從業務模型與架構設計兩個方面介紹訂單系統的構建過程。算法
1、訂單系統應具有怎樣的能力?sql
訂單打通用戶、商家、商品、庫存、售後等關鍵業務,是驅動交易全流程運轉的核心。而訂單系統承上啓下,做爲入口,涵蓋了訂單流程管理、庫存與營銷管理、算價引擎、履約子流程、售後以及退款信息管理等。數據庫
訂單系統具有的能力能夠按照下面三個角度進行切入拆解:小程序
用戶視角:支付算價、用戶下單、物流跟蹤、退款、訂單檢索等;緩存
商家視角:訂單狀態管理、商家拆單、售後客訴管理等;安全
平臺視角:拆單、CPS分傭、風控、反做弊等。性能優化
2、交易中臺如何提供服務?服務器
交易中臺基於現有的業務進行了抽象和歸類,從接入主要分紅以下三個類型:微信
通用型:業務本身維護商品,庫存信息等信息,只需調用訂單系統進行聚合支付。訂單系統會根據業務訴求提供營銷、退款、免密、推廣、分帳、一清、對帳、風控等功能支持,主要業務有:百度地圖、百度醫療、小度商城、度小店等。架構
自營型:訂單系統提供完整的電商能力支持,包含商品、庫存、營銷、退款、售後、檢索、物流跟蹤等功能支持,主要業務有:招財貓、誇誇豆等。
直連型:業務本身調用渠道進行支付,支付完成以後將訂單信息到同步到訂單系統,訂單系統經過計算推廣信息進行流量主(包含:主播、推廣做者、平臺等)分傭,主要業務有:噹噹、亞馬遜。
上圖中介紹了通用業務和自營業務關鍵環節中的對比。能夠看到:
通用業務主要側重於支付的環節,包含的步驟是支付、支付通知、交易狀態、退款等;
自營業務則在支付的基礎上,擴展增長了商品管理,包括收發貨、訂單的超時取消等;
直連業務是針對某些業務定製的,主要的區別在於資金流的處理環節,在此不進行贅述。
3、訂單的生命週期以及流程
在常見的電商環節中,訂單從產生以後,主要包括訂單確認、支付、發貨、成功、取消、退款等,這些狀態構成了一個有限狀態機。
這些狀態主要經過兩個動做進行串聯,即:訂單的正向流程及逆向流程,正向流程是指用戶購買產品或者服務的支付行爲管理。逆向流程則是指用戶發起售後形成的退款、退貨行爲管理。
3.1 正向支付流程
正向支付流程,由用戶發起,表明用戶向商家發起一筆交易,交易的流程入下圖所示:
入口:從上圖能夠看的比較明確,訂單能夠從多個入口產生,包括常見的移動設備、網站、掃碼等;
訂單生成:隨着用戶肯定訂單,訂單系統須要協調商品、營銷、庫存、風控等多個下游系統進行確認;
支付通道選擇:生成訂單以後,用戶會跳轉到支付界面,此時,訂單系統會提供常見的支付通道供用戶選擇進行處理,常見的支付通道微信、支付寶、度小滿支付等都由訂單系統進行集成;
支付成功:用戶支付成功以後,訂單系統會通知上下游系統狀態變動,同時對庫存、營銷進行扣減
商家發貨、確認收貨:訂單支付完成以後,就會進入商家處理流程,對於實體商品的購買場景,中臺訂單會進入物流環節。對於沒有實體的商品,中臺會提供沒有發貨流程的交易模板。
交易成功:交易成功是訂單的其中一個終態,表明用戶和商家最終完成交易。
3.2 逆向退款流程
在實際的業務場景中,逆向退款主要是指商家進行退款的流程。一般能夠在電商場景中的7天無理由退款、退貨、用戶售後流程中見到。
中臺通過抽象業務流程以後,梳理了一套退款退貨流程,如上圖所示,退款退貨發起以後,會進入商家審覈的環節,商家確認經過以後,會進行用戶處理。若是有物流環節的話,這時候會處理商家退貨發貨等。
業務流程就介紹到這裏,接下來主要作系統架構方面的介紹。
4、架構淺談
技術自己的目標是爲業務服務,貼合業務的技術架構自己是最經濟的選擇。訂單系統也是同樣,架構隨着業務的發展進新了逐步的優化和擴展。
在業務初期,架構以下圖所示:
業務初期規模較小,功能也比較單一,只須要具有簡單的支付、退款能力。全部功能都集中在一個系統,這樣作的好處是簡單快捷,容易部署,測試、開發效率高,是適合業務初期發展的架構。訂單系統初期也爲百度內部的火車票、小度商城、小程序的業務提供訂單管理的能力。
隨着業務不斷擴張,虛擬商品的購買,退款已經不能知足業務,須要擴展支持帶有物流商品訂單,而且在支付方式部分,須要擴展支持各種購買入口和場景,好比聚合掃碼支付、小額免密支付、週期代扣。隨着業務擴展,後續又引入了諸如直播帶貨、拍賣、閃電購、訂單評價、紅包搶購,資產充值等更加豐富的場景。而且在功能擴展以外,總體交易中臺還必須引入符合央行的監管規範改造,爲了訂單安全對接反做弊入口等諸多非功能方面的擴展。
爲了支持業務的多方向擴展,原來的單體架構在功能需求方面會遇到了擴展難的問題,同時在性能方面,也逐漸沒法知足吞吐量,響應時間以及擴展性等要求。
對於性能方面的擴展,須要將系統從單體改造爲分佈式的架構,這一部分的改造方案較爲成熟,採用集團內的分佈式數據庫、緩存、以及商業平臺近年來提供的雲原生部署架構能夠較爲快速的進行提高。惟一的難點就在於訂單分佈式數據庫的改造,因爲業務初期已經充分考慮了訂單的擴展進行持久層結構設計,這部分擴展也不難。
對於業務方面的擴展則是重頭戲,訂單系統構建了一套指令編排架構,經過不一樣指令調用不一樣的系統,而後抽象出模板,而後經過不一樣模板指令支撐不一樣業務場景。而且經過緩存,異步,降級等方式來提高性能。
分佈式技術改造方案很是成熟,不在此進行贅述。接下來主要介紹一下基於指令進行設計方案,以及基於該方案專門設計的性能升級改造。
4.1 指令編排架構
不一樣的產品形態、交易類型產生的流程各式各樣,爲了知足這種不一樣場景中的業務需求,訂單系統經過抽象了指令編排的設計,來實現業務流程的管理,從而使系統更具擴展性。
指令能夠簡單理解爲相對獨立的操做單元,好比常見的功能點均可以拆分爲指令集,好比支付指令、用戶指令等。優勢在於代碼的改動較小,遵循開閉原則。編排的方式相似於模板方法,不一樣的指令相似搭積木同樣的進行疊加,便可實現不一樣業務的流程。
實際的實現中,訂單系統將業務訴求拆解成不一樣的指令集,而且提供不一樣指令操做。
經過指令的組合造成規則,經過組合不一樣規則抽象出具體的模板,進行實例化從而產生具體的接入模型以供不一樣業務接入。
經過不一樣模板指令,能夠快速支撐不一樣業務場景。經過對複雜指令集的優化,還可使訂單系統的吞吐量,拓展性,穩定性都獲得很大提高。
下面列舉一個訂單拆分業務的案例進行說明。
拆單指令
用戶支付完訂單後,須要獲取訂單的支付信息,包括支付流水號、支付時間等。支付完訂單接着就是等商家發貨,但在發貨過程當中,根據平臺業務模式的不一樣,可能會涉及到訂單的拆分(若是是充值、消費類業務不存在發貨狀況)。
訂單拆分緣由通常分兩種:
(1)電商場景的合併支付:用戶商品來自不一樣商家,須要進行拆單進行分帳;
(2)問答、諮詢類場景:用戶支付時並不知道哪一個平臺或者答者會接單、回答。只有用戶提問最終完成服務以後才能肯定具體的商家,該場景也須要後續拆單。
這種業務能夠抽象爲拆單指令進行實現。
經過指令編排實現拆單
建立拆單指令能夠進行拆解,拆解爲兩種指令:拆單類型+拆單策略。根據業務需求經過指令的組合,抽象出規則並生成二種類型的模板(購物車拆單模板、諮詢問答類拆單模板)而且實例化出接入模型對外輸出。
當有購物車需求的業務能夠跟進接入類型選擇購物車拆單模板如:度小店。
當有諮詢問答或者支付後拆單需求的業務可使用諮詢問答拆單模板如:醫療,百度地圖,盎司手機充值等。
4.2 架構性能優化
上一章講解了指令編排在生產環境中承擔業務場景,接下來會講解指令編排架構遇到的問題,以及進行優化。
上圖是經過指令編排架構生成的一個通用下單模板,功能沒有問題,可是在較高流量的場景下,會遇到性能方面的挑戰。
能夠看出的問題有:
順序串行執行,調用鏈路過長,一旦中途一個指令執行出現問題,當次請求將返回失敗,穩定性沒法獲得保障;
每一個指令是獨立的執行單元,因此每一個指令都須要單獨查詢數據獲取所需的數據,形成對數據庫的請求成倍擴張。
針對問題,咱們逐個進行擊破。
4.2.1 長鏈路串行問題
首先是調用鏈過長問題作了如下優化。
針對數據變動不頻繁的數據進行緩存化,動態更新機制,使用緩存淘汰算法(LRU)。核心思想是「若是數據最近被訪問過,那麼未來被訪問的概率也更高」。如獲取用戶信息,商品信息接口。
針對非關鍵路徑的指令配置異步執行或者降級執行。經過異步線程池來實現指令異步化,經過指令執行時間來判斷是否降級處理。
經過以上二點完成調用鏈過長,穩定性沒法保障的問題。
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同步工具,將訂單信息同步到存儲之中來提供查詢。
另外,必定要保證數據查詢要存在索引,保證數據庫可控,能從長遠保證庫的擴展性和容量的提高。
5、總結
本文重點在介紹如何經過架構、技術實現等手段來搭建一個可靠、完善的訂單系統。實際生產中,應該抓住業務上的關鍵問題,在知足業務的前提下,對流程、需求作合理的減法,以下降總體架構的複雜度。另外,應該合理利用開源項目和第三方平臺服務知足系統需求,在技術方案和開發成本之間作到較好的折衷。
推薦閱讀 : |百億級流量的百度搜索中臺,是怎麼作可觀測性建設的?
閱讀原文:|百度交易中臺之訂單系統架構淺析
百度架構師:
百度官方技術公衆號上線啦!
技術乾貨 · 行業資訊 · 線上沙龍 · 行業大會
招聘信息 · 內推信息 · 技術書籍 · 百度周邊