業務中台
訂單中心
1)異步化
場景:大促期間新增許多需要獲取訂單狀態的服務,比如應對雙11而臨時增加的數據中台訂單大屏展示等
解決:異步化,並對消息隊列調優,多隊列分流
問題:注意異步化引發的亂序問題,一是傳輸階段,二是消費階段
rabbitmq傳輸:隊列級別順序保障,單消費者消費一個隊列可以嚴格保障順序性,需要擴充隊列數提
升性能
kafka傳輸:分區級別順序保障,只能保障投放和傳輸階段的順序性
consumer:1對1消費存在性能問題,接收消息后對key做二次分發,放入多個內存隊列,開啟多線程
消費
2)過期訂單
雙11搶單是最常見的場景,搶單不支付會占據大批量資源,如商品庫存。如何取消過期訂單是架構師必須面對的問題。主要有以下幾種方案:
掃表實現
原理:通過定時任務輪詢掃描訂單表,超時的批量修改狀態
優點:實現非常簡單
缺點:
- 大量數據集,對服務器內存消耗大。
- 數據庫頻繁查詢,訂單量大的情況下,IO是瓶頸。
- 存在延遲,間隔短則耗資源,間隔長則時效性差,兩者是一對矛盾。
- 不易控制,隨着定時業務的增多和細化,每個業務都要對訂單重復掃描,引發查詢浪費
java延遲隊列實現
原理:通過DelayQueue,每下一單,放入一個訂單元素並實現getDelay()方法,方法返回該元素距離失效還剩余的時間,當<=0時元素就失效,就可以從隊列中獲取到。啟用線程池對數據監聽,一旦捕獲失效訂單,取出之后,調用取消邏輯進行處理。
優點:基於jvm內存,效率高,任務觸發時間延遲低。
缺點:
- 存在jvm內存中,服務器重啟后,數據全部丟失。
- 依賴代碼硬編碼,集群擴展麻煩
- 依賴jvm內存,如果訂單量過大,無界隊列內容擴充,容易出現OOM
- 需要代碼實現,多線程處理業務,復雜度較高
- 多線程處理時,數據頻繁觸發等待和喚醒,多了無謂的競爭
消息隊列實現
原理:設置兩個隊列,每下一單放一條進延遲隊列,設定過期時間。消息一旦過期,獲取並放入工作隊列,由consumer獲取,喚起超時處理邏輯
如果采用的是RabbitMQ,其本身沒有直接支持延遲隊列功能,可以針對Queue和Message設置 x-
message-ttl,用消息的生存時間,和死信隊列來實現,具體有兩種手段, A: 通過隊列屬性設置,隊列
中所有消息都有相同的過期時間,粗粒度,編碼簡單 B: 對消息進行單獨設置,每條消息TTL可以不同,細粒度,但編碼稍微復雜。
優點:
- 可以隨時在隊列移除,實現實時取消訂單,及時恢復訂單占用的資源(如商品)
- 消息存儲在mq中,不占用應用服務器資源
- 異步化處理,一旦處理能力不足,consumer集群可以很方便的擴容
缺點:
- 可能會導致消息大量堆積
- mq服務器一旦故障重啟后,持久化的隊列過期時間會被重新計算,造成精度不足
- 死信消息可能會導致監控系統頻繁預警
redis實現
原理:利用redis的notify-keyspace-events,該選項默認為空,改為Ex開啟過期事件,配置消息監聽。每下一單在redis中放置一個key(如訂單id),並設置過期時間。
優點:
- 消息都存儲在Redis中,不占用應用內存
- 外部redis存儲,應用down機不會丟失數據
- 做集群擴展相當方便
- 依賴redis超時,時間准確度高
缺點:訂單量大時,每一單都要存儲redis內存,需要大量redis服務器資源
被動取消
原理:在每次用戶查詢訂單的時候,判斷訂單時間,超時則同時完成訂單取消業務。
優點:
- 實現極其簡單
- 不會有額外的性能付出
- 不依賴任何外部中間件,只是應用邏輯的處理
缺點:延遲度不可控,如果用戶一直沒觸發查詢,則訂單一直掛着,既不支付也未取消,庫存也就被占着
支付中心
支付交互流程
1)重復支付
原因:在第一步發起的時候,用戶進入支付方式選擇頁。選第一個支付方式並支付完后因為通知延遲,以為支付失敗。在支付又選了第二種,再次支付。
應對方案:
- 程序屏蔽,前端js觸發按鈕置灰或者遮罩提示(支付成功?遇到問題?),或者在支付方式選擇頁直接跳轉。
- 后端處理,發現不同通道下的支付成功回調,拋消息隊列或記錄日志。
數據修復:
首先查支付日志,確認針對同一筆訂單收到了不同支付渠道的回調。
其次,在支付平台管理后端可以查到入賬記錄,人工介入。
最后對賬階段會發現對方多帳,我方補單時出現重復訂單。
問題處理:調取退款接口或者在支付渠道的管理后台操作退款(一定要多次確認無誤)。
2)異常訂單
支付但未開單
場景:用戶明明支付成功,但未開通訂單
問題分析:
一般支付渠道會間隔性多次回調開單鏈接,如果支付未開單,銀行未回調的可能性比較小,着重排查開單接口是否可用。如果可用追查日志是否出現異常記錄。
應對措施:
- 對賬階段可以查漏,程序自動完成補單,但是處理相對延遲,取決於支付渠道的對賬文件下發周期
- 人工補單,人工查詢支付渠道后台數據,確認已支付的情況下,介入補單流程人工處理
未支付但已開單
場景:用戶未支付,或者財務中心未收到這筆款項,訂單狀態已開通。這種就問題比較嚴重了
應對措施:首先排除人為操作因素。其次排查系統是否存在漏洞或者級聯開單的情況
3)回調延遲
場景:用戶是期望支付完成的同時立馬看到結果。但是中間多層遠程的調用,可能發生訂單狀態更新延遲問題。
解決:主動查詢。在用戶查看訂單的時候,如果是類似“支付中”的中間態時,觸發遠程訂單狀態查詢接口。
4)支付路由
背景:
保障支付可用性及支付分流,支付中心對接多家渠道
方案:
- 支付中心對接多個支付渠道,支付寶,微信,各銀行或第三方支付供應商
- 對不同用戶,進入支付方式選擇頁時,做支付分流
- 做好監控統計,一旦某個支付渠道不可用或者延遲較大,切掉,下線,或者降權
營銷中心
1)概述
大促和活動不分家,一般營銷中心所面對的主要是促銷策略、優惠方式等業務上的架構問題。
從促銷活動的范圍來看,分為單品促銷活動、套裝促銷活動、店鋪促銷活動,平台促銷活動。
從促銷類型來看,分為滿減、折扣、贈品等。
業務復雜度高,一般遵循 “同類營銷僅可選其一,不同類營銷可疊加” 的規則。同類疊加意義不大且會造成系統復雜度上升,引發用戶困惑。
2)前端設計
用戶體驗上的設計,比如購物車里商品的排序,按商鋪分門別類。優惠總價格及時調整。這些依賴於前端的ui設計和交互體驗。
3)贈品設計
(SPU , SKU 基礎概念)
贈品有兩種設計方案,一種是不做單獨的SKU,只有一個空的描述,設計簡單,缺點是沒有商品詳情
頁,無法給用戶直觀的查看和估值。
另一種是單獨做SKU,贈品也會作為一個商品存在,與主商品關聯,下單的時候將會自動加到商品列
表,價格降為0。這種更為常見。整個商品有完善的詳情頁,用戶可以直接看到價格甚至單獨下單購
買。
4)排他與優先級
檢查同類別促銷,將最大優惠力度的規則應用到訂單,並且滿足排他性,同類只享受其一。比如滿10減3,滿20減5,那么用戶購買大於20時,只減5即可。
不同類別不做排斥,如購物車整體滿減后,不影響單個商品的折扣。在記錄數據時,優惠要細化到每個單獨的訂單明細上。退款也做到明細級別的單獨退。
5)價格分攤
滿減或平台券等優惠,在多個商品下單時,涉及到金額的分攤。即 優惠總額度/購物車總額 ,得到比例后再按比例均分到每個商品。只有分攤才能在發生部分退款時退回真實金額。
但是這會涉及到一個精度問題。舉例如下:滿99減9活動,假設用戶購買了 30+40+50=120,3件商品
應付111元。按比例折算的話,9/99取4位小數是0.9090,那么分攤后為
30x0.9090+40x0.9090+50x0.9090=109.08與實際支付金額出現偏差。這會造成財務無法平賬。
解決方案:記賬時在訂單明細記錄,將誤差 111-109.08=1.92計入金額最大的明細,也就是50元商品
上。那么最終記賬為:30x0.9090 + 40x0.9090 +(50*0.909+1.92)= 111
6)退單處理
退單后要同時恢復用戶的權益,比如優惠券的再次使用,限購次數等。確保用戶體驗。
商品中心
1)限時商品的下架控制
這個和超時訂單設計方案類似
2)庫存管理
普通商品可以直接借助數據庫鎖實現,一般分樂觀鎖和悲觀鎖兩種方案,如果采用悲觀鎖(如select語
句帶forupdate),會帶來很大的性能阻塞,所以更多的采用樂觀鎖設計。
樂觀鎖就是在最后執行庫存扣減操作時,將事務開始前獲取的庫存數量帶入到SQL語句中作為更新的
where條件,如果數量相等,則該條更新庫存的語句成功執行返回update條數為1;如果不相等,則表
示該商品的庫存信息已經被其他事務修改,需要放棄該條update的執行,采用重試處理。
庫存秒殺商品因為大批量的訪問在一瞬間涌入,數據庫扛不住。可以采用 redis緩存做decr處理,正常
下單后,再使用mq異步更新到db。(秒殺不超賣課題的庫存控制)