vivo全球商城:電商交易平臺設計實踐

陶然陶然發表於2023-04-13

   一、背景

  vivo官方商城經過了七年的迭代,從單體架構逐步演進到微服務架構,我們的開發團隊沉澱了許多寶貴的技術與經驗,對電商領域業務也有相當深刻的理解。

  去年初,團隊承接了O2O商城的建設任務,還有即將成立的禮品中臺,以及官方商城的線上購買線下門店送貨需求,都需要搭建底層的商品、交易和庫存能力。

  為節約研發與運維成本,避免重複造輪子,我們決定採用平臺化的思想來搭建底層系統,以通用能力靈活支撐上層業務的個性化需求。

  包括交易平臺、商品平臺、庫存平臺、營銷平臺在內的一整套電商平臺化系統應運而生。  

  本文將介紹交易平臺的架構設計理念與實踐,以及上線後持續迭代過程中的挑戰與思考。

   二、整體架構

  2.1 架構目標

  除了高併發、高效能、高可用這三高外,還希望做到:

  低成本

  注重模型與服務的可重用性,靈活支撐各業務的個性化需求,提高開發效率,降低人力成本。

  高擴充套件

  系統架構簡單清晰,應用系統間耦合低,容易水平擴充套件,業務功能增改方便快捷。

  2.2 系統架構

  (1)電商平臺整體架構中的交易平臺  

  (2)交易平臺系統架構  

  2.3 資料模型  

   三、關鍵方案設計

  3.1 多租戶設計

  (1)背景和目標

  交易平臺面向多個租戶(業務方),需要能夠儲存大量訂單資料,並提供高可用高效能的服務。

  不同租戶的資料量和併發量可能有很大區別,要能根據實際情況靈活分配儲存資源。

  (2)設計方案

  考慮到交易系統OLTP特性和開發人員熟練程度,採用MySQL作為底層儲存、ShardingSphere作為分庫分表中介軟體,將使用者標識(userId)作為分片鍵,保證同一個使用者的訂單落在同一個庫中。

  接入新租戶時約定一個租戶編碼(tenantCode),所有介面都要帶上這個引數;租戶對資料量和併發量進行評估,分配至少滿足未來五年需求的庫表數量。

  租戶與庫表的對映關係:租戶編碼 -> {庫數量,表數量,起始庫編號,起始表編號}。

  透過上面的對映關係,可以為每個租戶靈活分配儲存資源,資料量很小的租戶還能複用已有的庫表。

  示例一:

  新租戶接入前已有4庫*16表,新租戶的訂單量少且併發低,直接複用已有的0號庫0號表,對映關係是:租戶編碼-> 1,1,0,0

  示例二:

  新租戶接入前已有4庫*16表,新租戶的訂單量多但併發低,用原有的0號庫中新建8張表來儲存,對映關係是:租戶編碼-> 1,8,0,16

  示例三:

  新租戶接入前已有4庫*16表,新租戶的訂單量多且併發高,用新的4庫*8表來儲存,對映關係是:租戶編碼-> 4,8,4,0  

  使用者訂單所屬庫表計算公式

  庫序號 = Hash(userId) / 表數量 % 庫數量 + 起始庫編號表序號 = Hash(userId) % 表數量 + 起始表編號

  可能有小夥伴會問:為什麼計算庫序號時要先除以表數量?下面的公式會有什麼問題?

  庫序號 = Hash(userId) % 庫數量 + 起始庫編號表序號 = Hash(userId) % 表數量 + 起始表編號

  答案是,當庫數量和表數量存在公因數時,會存在傾斜問題,先除以表數量就能剔除公因數。

  以2庫4表為例,對4取模等於1的數,對2取模也一定等於1,因此0號庫的1號表中不會有任何資料,同理,0號庫的3號表、1號庫的0號表、1號庫的2號表中都不會有資料。

  路由過程如下圖所示:  

  (3)侷限性和應對辦法

  全域性唯一ID

  問題:分庫分表後,資料庫自增主鍵不再全域性唯一,不能作為訂單號來使用。且很多內部系統間的互動介面只有訂單號,沒有使用者標識這個分片鍵。

  方案:如下圖所示,參考雪花演算法來生成全域性唯一訂單號,同時將庫表編號隱含在其中(兩個5bit分別儲存庫表編號),這樣就能在沒有使用者標識的場景下,從訂單號中獲取庫表編號。  

  全庫全表搜尋

  問題:管理後臺需要根據各種篩選條件,分頁查詢所有滿足條件的訂單。

  方案:將訂單資料冗餘儲存一份到搜尋引擎Elasticsearch中,滿足各種場景下的快速靈活查詢需求。

  3.2 狀態機設計

  (1)背景

  之前做官方商城時,由於是定製化業務開發,各型別的訂單和售後單的狀態流轉都是寫死的,比如常規訂單在下單後是待付款,付款後是待發貨,發貨後是待收貨;虛擬商品訂單不需要發貨,沒有待發貨狀態。

  現在要做的是平臺系統,不可能再為每個業務方做定製化開發,否則會導致頻繁改動發版,程式碼錯綜冗餘。

  (2)目標

  引入訂單狀態機,能為每個業務方配置多套差異化的訂單流程,類似於流程編排。

  新增訂單流程時,儘可能不改動程式碼,實現狀態和操作的可複用性。

  (3)方案

  在管理後臺為每個租戶維護一系列訂單型別,資料轉化為JSON格式儲存在配置中心,或儲存在資料庫並同步到本地快取中。

  每個訂單型別的配置包括:初始訂單狀態,以及每個狀態下允許的操作和操作之後的目標狀態。

  當訂單在執行某個動作時,使用訂單狀態機來修改訂單狀態。

  訂單狀態機的公式是:

  StateMachine(E,S —> A , S’)

  表示訂單在事件E的觸發下執行動作A,並從原狀態S轉化為目標狀態S’

  每個訂單型別配置完成後,生成資料的結構是

  訂單商品行狀態機、售後單狀態機,也用同樣的方式實現

  3.3 通用操作觸發器

  (1)背景

  業務中通常都會有這樣的延時需求,我們之前往往透過定時任務來掃描處理。

  下單後多久未支付,自動關閉訂單

  申請退款後商家多久未稽核,自動同意申請

  訂單簽收後多久未確認收貨,自動確認收貨

  (2)目標

  業務方有類似的延時需求時,能夠有通用的方式輕鬆實現

  (3)方案

  設計通用操作觸發器,具體步驟為:

  配置觸發器,粒度是狀態機的流程型別。

  建立訂單/售後單時或訂單狀態變化時,如果有滿足條件的觸發器,傳送延遲訊息。

  收到延遲訊息後,再次判斷執行條件,執行配置的操作。

  觸發器的配置包括:

  註冊時間:可選訂單建立時,或訂單狀態變化時

  執行時間:可使用JsonPath表示式選取訂單模型中的時間,並可疊加延遲時間

  註冊條件:使用QLExpress配置,滿足條件才註冊

  執行條件:使用QLExpress配置,滿足條件才執行操作

  執行的操作和引數

  3.4 分散式事務

  對交易平臺而言,分散式事務是一個經典問題,比如:

  建立訂單時,需要同時扣減庫存、佔用優惠券,取消訂單時則需要進行回退。

  使用者支付成功後,需要通知發貨系統給使用者發貨。

  使用者確認收貨後,需要通知積分系統給使用者發放購物獎勵的積分。

  我們是如何保證微服務架構下資料一致性的呢?首先要區分業務場景對一致性的要求。

  (1)強一致性場景

  比如訂單建立和取消時對庫存和優惠券系統的呼叫,如果不能保證強一致,可能導致庫存超賣或優惠券重複使用。

  對於強一致性場景,我們採用Seata的AT模式來處理,下面的示意圖取自seata官方文件。  

  (2)最終一致性場景

  比如支付成功後通知發貨系統發貨,確認收貨後通知積分系統發放積分,只要保證能夠通知成功即可,不需要同時成功同時失敗。

  對於最終一致性場景,我們採用的是本地訊息表方案:在本地事務中將要執行的非同步操作記錄在訊息表中,如果執行失敗,可以透過定時任務來補償。  

  3.5 高可用與安全設計

  熔斷

  使用Hystrix元件,對依賴的外部系統新增熔斷保護,防止某個系統故障的影響擴大到整個分散式系統中。

  限流

  透過效能測試找出並解決效能瓶頸,掌握系統的吞吐量資料,為限流和熔斷的配置提供參考。

  併發鎖

  任何訂單更新操作之前,會透過資料庫行級鎖加以限制,防止出現併發更新。

  冪等性

  所有介面均具備冪等性,上游呼叫我們介面如果出現超時之類的異常,可以放心重試。

  網路隔離

  只有極少數第三方介面可透過外網訪問,且都有白名單、資料加密、簽名驗證等保護,內部系統互動使用內網域名和RPC介面。

  監控和告警

  透過配置日誌平臺的錯誤日誌報警、呼叫鏈的服務分析告警,再加上公司各中介軟體和基礎元件的監控告警功能,讓我們能夠能夠第一時間發現系統異常。

  3.6 其他考慮

  是否用領域驅動設計

  考慮到團隊非敏捷型組織架構,又缺少領域專家,因此沒有采用

  高峰期效能瓶頸問題

  大促和推廣期間,特別是爆款搶購時的流量可能會觸發限流,導致部分使用者被拒之門外。因為無法準確預估流量,難以提前擴容。

  可以透過主動降級方案增加併發量,比如同步入庫切為非同步入庫、db查詢轉為cache查詢、只能查到最近半年的訂單等。

  考慮到業務複雜度和資料量級還處在初期,團隊規模也難以支撐,這些設計有遠期計劃,但暫時還沒做。(架構的合適性原則,殺雞用牛刀,你願意也行)。

   四、總結與展望

  我們在設計系統時並沒有一味追求前沿技術和思想,面對問題時也不是直接採用業界主流的解決方案,而是根據團隊和系統的實際狀況來選取最合適的辦法。好的系統不是在一開始就被大牛設計出來的,而是隨著業務的發展和演進逐漸被迭代出來的。

  目前交易平臺已上線一年多,接入了三個業務方,系統執行平穩,公司內有交易/商品/庫存等需求的新業務,以及存量業務在遇到系統瓶頸需要升級時,都可以複用這塊能力。

  上游業務方數量的增加和版本的迭代,對平臺系統的需求源源不斷,平臺的功能得到逐漸完善,架構也在不斷演進,我們正在將履約模組從交易平臺中剝離出來,進一步解耦,為業務持續發展做好儲備。

來自 “ vivo網際網路技術 ”, 原文作者:vivo官網商城開發團隊;原文連結:http://server.it168.com/a2023/0413/6798/000006798645.shtml,如有侵權,請聯絡管理員刪除。

相關文章