得物面試:10wqps高併發,如何防止重複下單?

文章很長,且持續更新,建議收藏起來,慢慢讀!瘋狂創客圈總目錄 博客園版 爲您奉上珍貴的學習資源 :

免費贈送 :《尼恩Java面試寶典》 持續更新+ 史上最全 + 面試必備 2000頁+ 面試必備 + 大廠必備 +漲薪必備
免費贈送 :《尼恩技術聖經+高併發系列PDF》 ,幫你 實現技術自由,完成職業升級, 薪酬猛漲!加尼恩免費領
免費贈送 經典圖書:《Java高併發核心編程(卷1)加強版》 面試必備 + 大廠必備 +漲薪必備 加尼恩免費領
免費贈送 經典圖書:《Java高併發核心編程(卷2)加強版》 面試必備 + 大廠必備 +漲薪必備 加尼恩免費領
免費贈送 經典圖書:《Java高併發核心編程(卷3)加強版》 面試必備 + 大廠必備 +漲薪必備 加尼恩免費領

免費贈送 資源寶庫: Java 必備 百度網盤資源大合集 價值>10000元 加尼恩領取


得物面試:10wqps高併發,如何防止重複下單?

尼恩特別說明: 尼恩的文章,都會在 《技術自由圈》 公號 發佈, 並且維護最新版本。 如果發現圖片 不可見, 請去 《技術自由圈》 公號 查找
此文的公衆號版本 炸裂:MySQL死鎖是什麼,如何解決?

尼恩說在前面

在40歲老架構師 尼恩的讀者交流羣(50+)中,最近有小夥伴拿到了一線互聯網企業如得物、阿里、滴滴、極兔、有贊、希音、百度、網易、美團的面試資格,遇到很多很重要的面試題:

10wqps高併發,如何防止重複提交/支付訂單?

10wqps高併發,如何防止重複下單?

10wqps高併發,如何防止重複支付?

10wqps高併發,如何解決重複操作問題?

最近有小夥伴在面試得物,又遇到了這個的面試題。小夥伴支支吾吾的說了幾句,面試官不滿意,面試掛了。

所以,尼恩給大家做一下系統化、體系化的梳理,使得大家內力猛增,可以充分展示一下大家雄厚的 “技術肌肉”,讓面試官愛到 “不能自已、口水直流”,然後實現”offer直提”。

當然,這道面試題,以及參考答案,也會收入咱們的 《尼恩Java面試寶典PDF》V171版本,供後面的小夥伴參考,提升大家的 3高 架構、設計、開發水平。

最新《尼恩 架構筆記》《尼恩高併發三部曲》《尼恩Java面試寶典》的PDF,請關注本公衆號【技術自由圈】獲取,回覆:領電子書

基礎知識:電商訂單支付核心流程

首先,來看看 訂單支付的業務流程和交互流程。

圖解:訂單支付的業務流程和交互流程

結合下圖來看看 訂單支付的業務流程和交互流程。

訂單支付流程, 分爲 大致 的 6個步驟 :

1.下單/結算:

下單作爲支付的入口,但並非起點,

支付相關的金額等信息全部來至結算,此時訂單處於 未支付 狀態。

2.申請支付:

用戶開始申請支付,客戶端調用支付服務,

此時在支付系統內產生一筆訂單支付流水,這筆支付流水處於 未支付 狀態。

3.發起支付:

支付服務調用 第三方支付平臺,

通常, 第三方支付平臺 是 錢包類的支付方式,

在發起支付這一步驟,支付平臺會響應一些支付的鏈接,客戶端會對鏈接進行相應的處理。

4.錢包支付:

用戶進行支付,

用戶 APP端直接拉去錢包進行支付。

5.支付回調:

用戶完成支付之後,三方支付平臺會回調 商戶的支付服務 接口,通知支付結果。

6.更新訂單狀態:

支付服務 確認訂單支付完成後,會向 訂單服務同步 支付的結果。

訂單服務變更服務的狀態:未支付變更爲 待發貨

客戶端通過輪詢、長輪詢,或者服務端主動推送的方式,在界面上變更訂單狀態。

圖解:支付狀態的變化

如下圖,從支付流水角度來分析一下支付狀態的變化:

1.從未支付,到有支付結果的終態,中間還有一箇中間狀態:支付中

2.戶通過打開錢包--》完成支付--》支付回調,這段時間的支付流水就處於:支付中

重複下單的定義、危害、應對策略

什麼是重複下單

現在問題來了, 什麼是重複下單?

用戶在下單頁面進行下單時,由於用戶點擊下單按鈕 多次 、或者 重試策略 導致在訂單服務中接收到了 兩次同樣 的下單請求。

重複下單帶來的危害

重複下單場景,第N次的下單會對數據進行打亂,導致系統整體數據異常

  • 庫存數據異常
  • 金額數據異常
  • 優惠券數據異常
  • 等等

重複下單場景,第N次的下單需要等第一次下單操作完成

重複下單帶來的危害, 總結起來,有以下幾點:

1.系統資源佔用與性能下降
  • 重複下單會佔用系統資源,包括服務器、數據庫等,特別是在下單高峯期,可能導致系統性能下降,響應速度變慢。

  • 重複請求可能引發系統擁堵,影響其他正常用戶的購物體驗。

2.訂單處理複雜性增加
  • 商家在處理訂單時,需要花費額外的時間和精力去識別、合併或取消重複訂單,增加了訂單處理的複雜性。
  • 重複訂單可能導致庫存數量出現錯誤,進而影響後續訂單的履行。
3.財務結算與對賬難度增大
  • 重複下單可能導致財務結算時出現混亂,需要花費更多時間和精力去核對和調整賬目。
  • 對賬過程中需要區分哪些是重複訂單,哪些是有效訂單,增加了對賬的難度。
4.用戶體驗受損
  • 消費者在遇到重複下單時,可能會感到困惑和不滿,影響對電商平臺的信任度和忠誠度。
  • 重複下單可能導致消費者錯過優惠活動或促銷時機,影響其購物體驗。
5.數據異常與決策誤導
  • 重複下單的數據會干擾銷售數據的準確性,可能導致商家在決策時受到誤導。
  • 錯誤的銷售數據可能影響商家的庫存規劃、生產計劃等關鍵決策。
6.售後服務與退換貨問題
  • 如果消費者對重複下單的商品申請了退換貨,會增加售後服務的處理難度和成本。
  • 重複訂單可能導致退換貨政策執行混亂,影響消費者的售後體驗。
7.安全風險與欺詐行爲
  • 重複下單有時可能是惡意行爲,如刷單、欺詐等,給電商平臺帶來安全風險。
  • 需要重點加強對重複下單的監控和識別,以防範潛在的安全風險。

重複下單問題,主要解決辦法就是做好冪等,因爲在分佈式系統中,我們是沒有辦法保證用戶一定不會快速點擊兩次下單。

Order 服務調用 Pay 服務,剛好網絡超時,然後 Order 服務開始重試機制,於是 Pay 服務對同一支付請求,就接收到了兩次,而且因爲輪詢負載均衡算法,請求落在了不同業務服務節點,所以一個分佈式系統服務,須保證冪等性。

什麼場景下回發生重複下單?

場景1:客戶端bug

用戶短時間內多次點擊下單按鈕,或瀏覽器刷新按鈕導致。

比如下單的按鍵在點按之後,在沒有收到服務器請求之前,按鍵的狀態沒有設爲已禁用狀態,還可以繼續點擊。又或者,在觸摸屏下,用戶手指的點按可能被手機操作系統識別爲多次點擊。

場景2:超時重試

Nginx或Spring Cloud Gateway 網關層、RPC通信重試或業務層重試,進行超時重試導致的。

用戶的設備與服務器之間,可能是不穩定的網路。這樣一個下單請求過去,服務器不一定及時返回結果。

超時最大的問題:從用戶的角度,他無法確定下單的請求是否達到服務器,還是已經到了服務器但是返回結果時數據丟失了。所以用戶無法區分到底這個訂單是否下單成功。

場景3:用戶APP強退/閃退之後重新下單

心急的用戶可能會重啓流程/重啓App/重啓手機。在這種強制的手段下,任何技術手段都會失效。

場景4:黑客或惡意用戶

黑客或惡意用戶使用postman等網絡工具,重複惡意提交訂單。

重複下單問題與冪等性問題

重複下單問題,本質上,就是下單操作的冪等性問題

說到底,“下單防重”的問題是屬於“接口冪等性”的問題範疇。

什麼是冪等性問題?

所謂冪等性,就是一次操作和多次操作同一個資源,所產生的影響均與一次操作的影響相同。

"冪等(idempotent、idempotence)是一個數學與計算機學概念,常見於抽象代數中。

冪等函數,或冪等方法,是指可以使用相同參數重複執行,並能獲得相同結果的函數。

冪等性,用數學語言表達就是:

f(x)=f(f(x))

維基百科的冪等性定義如下:

冪等(idempotent、idempotence)是一個數學與計算機學概念,常見於抽象代數中。

冪等函數,或冪等方法,是指可以使用相同參數重複執行,並能獲得相同結果的函數。

這些函數不會影響系統狀態,也不用擔心重複執行會對系統造成改變。

例如,“setTrue()”函數就是一個冪等函數,無論多次執行,其結果都是一樣的,更復雜的操作冪等保證是利用唯一交易號(流水號)實現.

通俗點說:

一個接口如果冪等,不管被調多少次,只要參數不變,結果也不變。

冪等性是對於寫操作來說的,一個寫操作,一般都需要保證:

  • 冪等性

  • 可用性

  • ACID事務屬性。

上述內容選自尼恩這篇硬核文章:

最系統的冪等性方案:一鎖二判三更新

如何解決接口冪等問題

接口接口冪等問題,只需記住一句口令:一鎖、二判、三更新。只需嚴格按照這個過程,那麼就可以解決接口冪等問題,總結如下:

1.一鎖:先加鎖,可以加分佈式鎖、悲觀鎖都可以,但是一定是一個互斥鎖。

2.二判:進行冪等性判斷,可以基於狀態機、業務流水錶、數據庫唯一索引等,進行重複操作的判斷。

3.三更新:對數據進行更新,將數據進行持久化。

關於冪等性方案,請參見尼恩這篇硬核文章:

最系統的冪等性方案:一鎖二判三更新

如何解決重複下單問題?

方案一:提交訂單按鈕置灰

防止用戶提交,最常規的做法,就是客戶端點擊下單之後,在收到服務端響應之前,按鈕置灰。

前端頁面直接防止用戶重複提交表單,但網絡錯誤會導致重傳,很多RPC框架、網關都有自動重試機制,所以重複請求在前端側無法完全避免。

當然,這種方案也不是真的沒有價值。

這種方案可以在高併發場景下,從瀏覽器端去攔住一部分請求,減少後端服務器的處理壓力,達到過濾流量的效果。

方案一優點:簡單。基本可以防止重複點擊提交按鈕造成的重複提交問題。

方案一缺點:前進後退操作,或者F5刷新頁面等問題並不能得到解決。

方案二:請求唯一ID+數據庫唯一索引約束

首先來向大家介紹一種最簡單的、成本最低的解決方案。

防重是第一步,需要識別是否重複請求,

所以,需要客戶端在請求下單接口的時候,需要生成一個唯一的請求號:requestId,服務端拿這個請求號,判斷是否重複請求。

核心流程圖:

實現的邏輯,流程如下:

  1. 當用戶進入訂單提交界面的時候,調用後端獲取請求唯一ID,並將唯一ID值埋點在頁面裏面。

  2. 當用戶點擊提交按鈕時,後端檢查這個唯一ID是否用過,如果沒有用過,繼續後續邏輯;如果用過,就提示重複提交。

  3. 最關鍵的一步操作,就是把這個唯一ID 存入業務表中,同時設置這個字段爲唯一索引類型,從數據庫層面做防止重複提交。

對於下單流量不算高的系統,可以採用這種 請求唯一ID + 數據表增加唯一索引約束`的方式,來防止接口重複提交

但是這個併發量太低,10wqps高併發, 這個根本沒法滿足。

方案三:reids分佈式鎖+請求唯一ID

在上一個方案中,我們詳細的介紹了對於下單流量不算高的系統,可以通過 請求唯一ID+數據表增加唯一索引約束`這種方案來實現防止接口重複提交

隨着業務的快速增長,每一秒的下單請求次數,可能從幾十上升到幾百甚至幾萬。

面對這種下單流量越來越高的場景,此時數據庫的訪問壓力會急劇上升,數據庫會成爲整個下單流程的瓶頸。

對於這樣的場景,我們可以選擇引入緩存中間件來緩解數據庫高併發場景下的壓力,

下面,我們以引入redis緩存中間件,向大家介紹具體的解決方案。

流程如下:

  1. 當用戶進入訂單提交界面的時候,調用後端獲取請求唯一 ID,同時後端將請求唯一ID存儲到redis中再返回給前端,前端將唯一 ID 值埋點在頁面裏面。

  2. 當用戶點擊提交按鈕時,後端檢查這個請求唯一 ID 是否存在,如果不存在,提示錯誤信息;如果存在,繼續後續檢查流程。

  3. 使用redis的分佈式鎖服務,對請求 ID 在限定的時間內進行加鎖,如果加鎖成功,繼續後續流程;如果加鎖失敗,提示說明:服務正在處理,請勿重複提交。

  4. 最後一步,如果加鎖成功後,需要將鎖手動釋放掉,以免再次請求時,提示同樣的信息;同時如果任務執行成功,需要將redis中的請求唯一 ID 清理掉。

至於數據庫是否需要增加字段唯一索引,理論上可以不用加,如果加了更保險。

這個通過擴展,可以滿足 10wqps高併發要求。

具體的擴展方案, 即將在 《尼恩Java面試寶典》 配套視頻 發佈。

方案四:reids分佈式鎖+token

在上一個方案中,每次提交訂單的時候,都需要調用服務端獲取請求唯一ID:requestId,然後才能提交,這裏面存在以下問題:

下單鏈路中,多了的一次請求, 這一次請求專門用於請求 request id。這次請求是否可以減少呢?

當然是可以的,比如, 可以用戶的請求的特徵數據,根據特定規則生成token,來替代 那個專用的 request id。

而不用專門去來減少一次客戶端與服務端之間的交互次數,提高下單流程效率。

特定規則生成token, 比如說,可以組合一些核心參數,去生成token, 核心參數包括:
應用名+接口名+方法名+請求參數簽名(請求header、body參數,取SHA1值)

組合一些核心參數,去生成token ,大致 流程如下:

  1. 用戶點擊提交按鈕,服務端接受到請求後,通過規則計算出本次請求唯一ID值

  2. 使用redis的分佈式鎖服務,對請求 ID 在限定的時間內嘗試進行加鎖,如果加鎖成功,繼續後續流程;如果加鎖失敗,說明服務正在處理,請勿重複提交。

  3. 最後一步,如果加鎖成功後,需要將鎖手動釋放掉,以免再次請求時,提示同樣的信息。

方案四和方式三的最大不同,在於 唯一請求 ID 的生成 環節,

方案四 放在服務端通過組合來實現 唯一請求 ID 的生成 ,在保證防止接口重複提交的效果同時,也可以顯著的降低接口測試複雜度!

方案四的性能,比方案三更高。

方案五:技術+產品+運營支持

如果經過上述方案處理,還是會有用戶誤操作,直到收到兩份商品才發現下重了。

在實際設計中,無論多麼好的技術,也不可能100%的攔截所有的可能性,必須依靠技術+產品設計+運營支持的綜合手段才能解決這類問題。

此時就得依靠運營/客服的支持了。

所以即便京東這一類電商等也是配合運營手段進行處理。

實操:reids分佈式鎖+token 解決重複下單的問題

只講理論,是耍流氓

40歲老架構師一直強調, 實操,實操,實操纔是王道

比如咱們社羣的 k8s 實操:

比如咱們社羣的 AT+TCC模式混合事務實操 ):

此實操即將配合 《尼恩Java面試寶典視頻》發佈

接下來,咱們開始 reids分佈式鎖+token 解決重複下單的問題的實操

此實操即將配合 《尼恩Java面試寶典視頻》發佈

實操step1:使用AOP進行 BizToken 的無入侵生成

定義一個註解 BizToken

在業務層或者 控制層,進行BizToken 的使用

實操step2:編寫服務驗證邏輯,通過 aop 代理方式實現

此 aop 切面的 具體的實操演示,請參見 《尼恩Java面試寶典》 視頻

實操step3:使用redission分佈式鎖保證冪等

在BizToken校驗邏輯用到了redis分佈式鎖保證冪等,

redission分佈式鎖 具體實現邏輯如下:

通過封裝 redission的分佈式鎖來實現 鎖的功能:

具體的實現,委託到 redission的分佈式鎖來實現

具體的實操演示,請參見 《尼恩Java面試寶典》 視頻

10wqps高併發,防止重複下單總結

防止重複下單,本質上就是先做重複判斷,然後服務端做好冪等性控制,結合實際業務場景選擇相應的方案。

實現冪等性需要先理解自身業務需求,根據業務邏輯來實現這樣才合理,處理好其中的每一個結點細節,完善整體的業務流程設計,才能更好的保證系統正常運行。

說在最後:有問題找老架構取經

10wqps高併發,如何防止重複下單?

如果大家能對答如流,如數家珍,基本上 面試官會被你 震驚到、吸引到。

最終,讓面試官愛到 “不能自已、口水直流”。offer, 也就來了。

在面試之前,建議大家系統化的刷一波 5000頁《尼恩Java面試寶典PDF》,裏邊有大量的大廠真題、面試難題、架構難題。很多小夥伴刷完後, 吊打面試官, 大廠橫着走。

在刷題過程中,如果有啥問題,大家可以來 找 40歲老架構師尼恩交流。

另外,如果沒有面試機會,可以找尼恩來改簡歷、做幫扶。

遇到職業難題,找老架構取經, 可以省去太多的折騰,省去太多的彎路。

尼恩指導了大量的小夥伴上岸,前段時間,尼恩指導一個40歲+被裁小夥伴,拿到了一個年薪100W的offer。

狠狠卷,實現 “offer自由” 很容易的, 前段時間一個武漢的跟着尼恩捲了2年的小夥伴, 在極度嚴寒/痛苦被裁的環境下, offer拿到手軟, 實現真正的 “offer自由” 。

技術自由的實現路徑:

實現你的 架構自由:

喫透8圖1模板,人人可以做架構

10Wqps評論中臺,如何架構?B站是這麼做的!!!

阿里二面:千萬級、億級數據,如何性能優化? 教科書級 答案來了

峯值21WQps、億級DAU,小遊戲《羊了個羊》是怎麼架構的?

100億級訂單怎麼調度,來一個大廠的極品方案

2個大廠 100億級 超大流量 紅包 架構方案

… 更多架構文章,正在添加中

實現你的 響應式 自由:

響應式聖經:10W字,實現Spring響應式編程自由

這是老版本 《Flux、Mono、Reactor 實戰(史上最全)

實現你的 spring cloud 自由:

Spring cloud Alibaba 學習聖經》 PDF

分庫分表 Sharding-JDBC 底層原理、核心實戰(史上最全)

一文搞定:SpringBoot、SLF4j、Log4j、Logback、Netty之間混亂關係(史上最全)

實現你的 linux 自由:

Linux命令大全:2W多字,一次實現Linux自由

實現你的 網絡 自由:

TCP協議詳解 (史上最全)

網絡三張表:ARP表, MAC表, 路由表,實現你的網絡自由!!

實現你的 分佈式鎖 自由:

Redis分佈式鎖(圖解 - 秒懂 - 史上最全)

Zookeeper 分佈式鎖 - 圖解 - 秒懂

實現你的 王者組件 自由:

隊列之王: Disruptor 原理、架構、源碼 一文穿透

緩存之王:Caffeine 源碼、架構、原理(史上最全,10W字 超級長文)

緩存之王:Caffeine 的使用(史上最全)

Java Agent 探針、字節碼增強 ByteBuddy(史上最全)

實現你的 面試題 自由:

4800頁《尼恩Java面試寶典 》 40個專題

免費獲取11個技術聖經PDF:

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章