25. 谷粒商城订单系统
簡介
電商系統(tǒng)涉及到 3 流,分別時信息流,資金流,物流,而訂單系統(tǒng)作為中樞將三者有機的集合起來。
訂單模塊是電商系統(tǒng)的樞紐,在訂單這個環(huán)節(jié)上需求獲取多個模塊的數(shù)據(jù)和信息,同時對這些信息進行加工處理后流向下個環(huán)節(jié),這一系列就構成了訂單的信息流通。
訂單構成
1、 用戶信息
用戶信息包括用戶賬號、用戶等級、用戶的收貨地址、收貨人、收貨人電話等組成,用戶賬戶需要綁定手機號碼,但是用戶綁定的手機號碼不一定是收貨信息上的電話。用戶可以添加多個收貨信息,用戶等級信息可以用來和促銷系統(tǒng)進行匹配,獲取商品折扣,同時用戶等級
還可以獲取積分的獎勵等
2 、訂單基礎信息訂單基礎信息是訂單流轉的核心,其包括訂單類型、父/子訂單、訂單編號、訂單狀態(tài)、訂單流轉的時間等。
(1)訂單類型包括實體商品訂單和虛擬訂單商品等,這個根據(jù)商城商品和服務類型進行區(qū)分。
(2)同時訂單都需要做父子訂單處理,之前在初創(chuàng)公司一直只有一個訂單,沒有做父子訂單處理后期需要進行拆單的時候就比較麻煩,尤其是多商戶商場,和不同倉庫商品的時候,父子訂單就是為后期做拆單準備的。
(3)訂單編號不多說了,需要強調的一點是父子訂單都需要有訂單編號,需要完善的時候可以對訂單編號的每個字段進行統(tǒng)一定義和詮釋。
(4)訂單狀態(tài)記錄訂單每次流轉過程,后面會對訂單狀態(tài)進行單獨的說明。
(5)訂單流轉時間需要記錄下單時間,支付時間,發(fā)貨時間,結束時間/關閉時間等等
3、 商品信息
商品信息從商品庫中獲取商品的 SKU 信息、圖片、名稱、屬性規(guī)格、商品單價、商戶信息等,從用戶下單行為記錄的用戶下單數(shù)量,商品合計價格等。
4、優(yōu)惠信息
優(yōu)惠信息記錄用戶參與的優(yōu)惠活動,包括優(yōu)惠促銷活動,比如滿減、滿贈、秒殺等,用戶使用的優(yōu)惠券信息,優(yōu)惠券滿足條件的優(yōu)惠券需要默認展示出來,具體方式已在之前的優(yōu)惠券篇章做過詳細介紹,另外還虛擬幣抵扣信息等進行記錄。
為什么把優(yōu)惠信息單獨拿出來而不放在支付信息里面呢?
因為優(yōu)惠信息只是記錄用戶使用的條目,而支付信息需要加入數(shù)據(jù)進行計算,所以做為區(qū)分。
5、支付信息
(1)支付流水單號,這個流水單號是在喚起網(wǎng)關支付后支付通道返回給電商業(yè)務平臺的支付流水號,財務通過訂單號和流水單號與支付通道進行對賬使用。
(2)支付方式用戶使用的支付方式,比如微信支付、支付寶支付、錢包支付、快捷支付等。支付方式有時候可能有兩個——余額支付+第三方支付。
(3)商品總金額,每個商品加總后的金額;運費,物流產(chǎn)生的費用;優(yōu)惠總金額,包括促銷活動的優(yōu)惠金額,優(yōu)惠券優(yōu)惠金額,虛擬積分或者虛擬幣抵扣的金額,會員折扣的金額等之和;實付金額,用戶實際需要付款的金額。用戶實付金額=商品總金額+運費-優(yōu)惠總金額
6、物流信息
物流信息包括配送方式,物流公司,物流單號,物流狀態(tài),物流狀態(tài)可以通過第三方接口來獲取和向用戶展示物流每個狀態(tài)節(jié)點
訂單狀態(tài)
用戶提交訂單后,訂單進行預下單,目前主流電商網(wǎng)站都會喚起支付,便于用戶快速完成支付,需要注意的是待付款狀態(tài)下可以對庫存進行鎖定,鎖定庫存需要配置支付超時時間,超時后將自動取消訂單,訂單變更關閉狀態(tài)。
用戶完成訂單支付,訂單系統(tǒng)需要記錄支付時間,支付流水單號便于對賬,訂單下放到 WMS系統(tǒng),倉庫進行調撥,配貨,分揀,出庫等操作。
倉儲將商品出庫后,訂單進入物流環(huán)節(jié),訂單系統(tǒng)需要同步物流信息,便于用戶實時知悉物品物流狀態(tài)
用戶確認收貨后,訂單交易完成。后續(xù)支付側進行結算,如果訂單存在問題進入售后狀態(tài)
付款之前取消訂單。包括超時未付款或用戶商戶取消訂單都會產(chǎn)生這種訂單狀態(tài)。
用戶在付款后申請退款,或商家發(fā)貨后用戶申請退換貨。售后也同樣存在各種狀態(tài),當發(fā)起售后申請后生成售后訂單,售后訂單狀態(tài)為待審核,等待商家審核,商家審核通過后訂單狀態(tài)變更為待退貨,等待用戶將商品寄回,商家收貨后訂單狀態(tài)更新為待退款狀態(tài),退款到用戶原賬戶后訂單狀態(tài)更新為售后成功。
訂單流程
遠程調用會員服務
遠程調用購物車
查詢用戶積分
計算應付、總付
防重令牌
Feign遠程調用丟失請求頭問題
解決
加上 feign 遠程調用的請求攔截器,在每次發(fā)送遠程請求之前,把老請求的數(shù)據(jù)同步過來,這樣就可以解決請求頭的丟失問題了。
異步任務丟失上下文問題
獲取address、cart所使用的線程,與主任務的線程不同,所以異步任務無法獲取主任務的上下文環(huán)境。
解決
將主線程中原始請求的上下文數(shù)據(jù)共享出來,在新開啟的異步任務中重新設置上下文數(shù)據(jù),即可解決
下單流程
鎖庫存流程
本地事務在分布式下的問題
1、假失敗
如果保存訂單成功,遠程鎖庫存假失敗,那就會出現(xiàn)問題
假失敗就是我們在訂單服務調庫存服務時, 庫存鎖定成功,然后由于服務器慢、卡頓、等故障原因,本地事務提交了之后,一直沒返回到訂單服務
此時再看訂單服務,因為調用庫存服務時間太長了,庫存服務遲遲沒有返回結果,可能就會觸發(fā) feign 的超時機制,在調用遠程服務這里拋異常:read time out 讀取超時,但是這個異常并不是我們手動拋的鎖庫存異常,而是 feign 的異常
并且訂單服務,設計的回滾機制,是只要一出現(xiàn)異常就會全部回滾,
結果:庫存鎖定成功,訂單服務因為 feign 的超時機制,出現(xiàn)異常,導致訂單數(shù)據(jù)全部回滾,最終數(shù)據(jù)不一致
2、調用新服務出現(xiàn)異常之后,已經(jīng)執(zhí)行的服務不會回滾
假設庫存鎖定成功,將結果返回到了訂單服務,我們根據(jù)結果又調用了積分服務,讓它扣減積分,
結果積分服務內部出現(xiàn)異常,積分數(shù)據(jù)回滾
此時再看訂單服務,訂單服務感知到我們手動拋的積分異常,訂單數(shù)據(jù)回滾,但是庫存服務,卻不會有任何感知,
結果:積分、訂單數(shù)據(jù)全部回滾,庫存給鎖定了,也是數(shù)據(jù)不一致
只需要在訂單服務的庫存執(zhí)行成功之后,添加一個 int i = 10 / 0;,模擬積分服務出現(xiàn)異常,很容易就能復現(xiàn)這個問題
總結
本地事務,在分布式系統(tǒng),只能控制住自己的回滾,控制不了其他服務的回滾
產(chǎn)生分布式事務的最大原因,就是網(wǎng)絡問題 + 分布式機器
如何解決下單系統(tǒng)這個高并發(fā)里邊的分布式事務呢?
首先,我們肯定不會用 2PC 模式、 TCC-事務補償性方案,我們也不考慮
最終我們選擇了可靠消息+最終一致性這種方式
為了保證高并發(fā),訂單這一塊還是自己回滾,
庫存服務自己怎么回滾?
有兩種解決辦法
第一種
我們在提交訂單那里,當捕捉到異常要回滾的時候,給庫存服務發(fā)一個消息,讓庫存服務自己把庫存解鎖
這樣不需要讓庫存事務回滾,只需要給它發(fā)一個消息,不會損失什么性能
第二種
庫存服務本身也可以使用自動解鎖模式。
怎么自動解鎖呢?
需要使用消息隊列來完成。
如果你想讓我這的哪些庫存解鎖,首先你需要給我發(fā)一個消息告訴我。
然后我們專門的庫存解鎖服務,去來訂閱我們stock.release.stock.queue這個隊列里的消息。
那你給我發(fā)消息的時候,比如:用路由鍵stock.release,我知道要庫存解鎖,
然后,你的消息發(fā)給我們這個交換機stock-event-exchange。
交換機把這個消息路由給stock.release.stock.queue這個隊列。
然后,這個隊列stock.release.stock.queue里邊存的這些消息都是庫存要解鎖的消息,我們的庫存解鎖服務只要收到了,它就會在后臺慢慢的解鎖消息。
我們不用保證強一致,我們哪怕是二十分鐘、三十分鐘,乃至于一天以后把這個庫存解鎖了,最終一致了就行。
所以我們可以來使用消息隊列來完成我們的這個最終一致性。
鎖庫存的增強版邏輯
我們想要鎖庫存的話,我們先來保存一個庫存工作單和庫存工作單詳情
相當于只要我們想要鎖庫存,我們先給數(shù)據(jù)庫里邊保存記錄,我要鎖庫存。
接下來我們就來進行鎖,只要鎖成功了,那一切ok。
如果鎖失敗了,數(shù)據(jù)庫里邊相當于沒有這個鎖庫存記錄。
因為鎖失敗呢,我們這個本身自己所失敗會全部回滾。
但如果可能是這種失敗,比如我們來到訂單里邊,我們庫存其實自己鎖成功了。但是我們訂單下邊的其他完了,然后庫存要進行解鎖。那怎么辦呢?
我們可以使用定時任務
訂單服務的完整消息隊列
庫存自動解鎖
庫存微服務,有一個它的庫存交換機stock-event-exchange.
如果想要解鎖庫存,應該是這樣的。
首先訂單創(chuàng)建成功之后,庫存鎖定成功,然后發(fā)一個消息給交換機,
這個消息里面的內容有訂單編號、倉庫編號、哪個商品鎖了幾個庫存,
這個交換機,綁定了兩個隊列,
一個是按照stock.release.#模糊匹配的路由鍵綁定的stock.release.stock.queue隊列
一個是stock.delay.queue隊列
第一次發(fā)的庫存鎖定成功的消息,先使用路由鍵叫stock.locked
交換機按照這個路由鍵,找到stock.delay.queue延時隊列
延時隊列50分鐘以后,用stock.release這個路由鍵,將死信交給庫存交換機stock-event-exchange,
交換機收到以后,按照這個路由鍵查找,發(fā)現(xiàn)stock.release.#這個模糊匹配的路由鍵跟它是一樣的,然后被交換機路由到我們這個stock.release.stock.queue隊列。
接下來的解鎖庫存服務,專門來處理stock.release.stock.queue里的消息。
最終實現(xiàn)
7.24
7.23
具體代碼
定時關閉訂單
首先訂單創(chuàng)建成功之后,使用order.create.order路由鍵將消息路由到order-event-exchange交換機
交換機發(fā)現(xiàn)order.create.order這個路由鍵綁定的是order.delay.queue這個延時隊列,然后就把它放到order.delay.queue隊列里
過了30分鐘,這個延時隊列里面的消息,也就是死信,通過order.release.order又路由回order-event-exchange交換機
然后交換機發(fā)現(xiàn)這個路由鍵對應的是order.release.order.queue這個隊列,然后就放到order.release.order.queue這個隊列里
最終監(jiān)聽order.release.order.queue這個隊列的釋放訂單服務,發(fā)現(xiàn)有消息進來了,就會針對里面的數(shù)據(jù)對其進行關閉訂單
問題
這種關閉訂單方式會有一些問題
假設訂單創(chuàng)建成功之后,訂單服務的機器由于卡頓、消息延遲等原因,導致訂單未及時取消
此時庫存服務的邏輯是訂單創(chuàng)建成功之后,它自己會發(fā)一個消息,等 40分鐘 以后檢查之前下單的訂單是否已取消,如果是已取消,則解鎖庫存
結果,庫存服務過來查詢時,訂單服務由于上述原因沒有將訂單修改為已取消,所以庫存就不會解鎖,此時的庫存消息就算是消費了
等庫存服務都檢查完了,此時的訂單服務才反應過來,然后把訂單狀態(tài)改為已取消了,但是此時庫存服務會再有任何的操作了,因為檢查訂單的消息已經(jīng)被消費了,庫存永遠得不到解鎖。
解決
為了解決這個問題,我們在監(jiān)聽取消訂單的消息時,再發(fā)一個消息,主動解鎖庫存。
主動解鎖庫存
具體是這樣的,在釋放訂單之后,我們主動發(fā)一個消息解鎖庫存,
使用order.release.other將消息路由到交換機,
交換機根據(jù)order.release.other.#匹配到stock.release.stock.queue這個隊列,并將消息發(fā)了過去,
庫存服務有對這個隊列進行監(jiān)聽,所有一旦有數(shù)據(jù)來了,就會對其進行解鎖庫存服務
總結
以上是生活随笔為你收集整理的25. 谷粒商城订单系统的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 从github安装python项目
- 下一篇: VS2017+海康威视工业相机调用查找不