上週的時候有幸和京東大佬來了次線上“交流”(他問我答那種,懂的都懂),由於我下午臨時有個會議要參加,原本計劃1小時的“交流”縮短到30分鐘,前二十分鐘聊了聊專案,距離我開會還剩十分鐘時,大佬突然問我道:假設有這麼一個場景,使用者下完單之後沒有支付,然後30分鐘之後訂單自動取消了,你有什麼設計思路去實現這個功能呢?
我在那一邊盯著手錶一邊思考這個問題,咦,工作多載,做過訂單相關的但沒整過支付啊,網上看過一些支付的實現調一些支付寶、微信支付的介面一類的方案,但是大佬的這個問題的重點顯然不在問我怎麼調API,而是訂單超時自動取消啊!大腦快快想。
沉思十幾秒左右,腦子裡瞬時冒出了第一種方案,完全可以用定時JOB來實現啊,於是整理下思路便說道:“我們可以採用定時JOB方式去資料庫中檢查訂單的狀態,大概可以這樣設計:1.首先設計一個資料庫表,用於儲存訂單資訊,包括訂單生成時間、支付狀態等欄位。2.然後後端開啟一個定時JOB,定期(如10秒鐘)掃描資料庫中的訂單。3.對於每個訂單,檢查其支付狀態以及訂單生成時間和當前時間相比是否超過了30分鐘。4如果訂單未支付且時間差超過30分鐘,則更新訂單狀態為已取消,並執行相應的取消邏輯,比如釋放庫存。”
京東大佬沉默了幾秒,沒有評價對與錯,說道:“這可以作為一種實現方案,有沒有更好的方案呢?”
更好的方案??我又開始催促我那每天被工作累死的腦細胞趕緊想了,還好,立馬想了一個我自己都覺得不靠譜的方案。
我有點憋笑似的回答到:“也可以不使用定時JOB,使用者去查詢訂單的時候,肯定要請求後端介面麼,那時候做處理邏輯,針對性的再去判斷自己訂單有沒有超時,超時的話直接修改狀態,增加庫存,將最新的結果反饋的頁面上,而且伺服器還沒那麼多的壓力。”坦白講,我當時居然還覺得自己的這個方案挺天才,顯然,這個答案並不是大佬想聽到的答案,還是問我有沒有更好的思路。
還剩3分鐘會議就要開始了,我實在沒時間去想第三種方案了,便和大佬道了歉,得抓緊去開會了,大佬也比較理解,又和我另約了其他的時間再繼續溝通。
於是回去開會、繼續工作。。。
晚上下班的路上,我靜下心來思索著白天的回答,發現自己說的這兩種方案真的一個也不靠譜:首先第一種定時任務輪詢資料庫,為了減少和30分鐘的誤差,我們必然要把這個輪詢訂單的間隔時間配置的非常短,幾秒那種,京東那麼大的公司,每天產生那麼都訂單,用定時一直去訪問DB中訂單表,那必然對DB造成的壓力太大,甚至會影響到別的業務,效能實在是太低了,怎麼可能用這種方案呢? 再說第二種方案,看起來對伺服器效能沒損耗,而且對使用者的感知也做到了開啟頁面就知道訂單取消了,可是有個問題啊,假設使用者不開啟訂單頁面咋整,那庫存豈不是永遠無法釋放了?一個使用者沒有及時開啟購物APP看,成千上萬個呢?若是京東真是使用的我這第二種“天才方案”,那得錯過多少假庫存不足引起的無法交易呢。 總而言之,兩種方案都不是最優解!
反思過後,想了想其實可以使用一些延時處理的策略來實現,於是查了一些資料,下面是我整理的一些設計思路:
- 利用延時佇列:DelayQueue:
- DelayQueue是一個無界阻塞佇列,其中的元素只能在其延遲期滿時才能從佇列中獲取。
- 將訂單作為物件放入DelayQueue,物件的延遲時間設定為30分鐘。
- 使用生產者執行緒將新訂單放入DelayQueue。
- 消費者執行緒從DelayQueue中獲取訂單,如果訂單在30分鐘內未被支付,則執行取消操作。
- 定時任務 + 使用Redis的過期鍵:
- Redis支援設定鍵的過期時間,當鍵過期時,Redis會自動刪除該鍵。
- 將訂單資訊儲存為Redis中的鍵,鍵的過期時間設定為30分鐘。
- 定時掃描Redis,將對DB的壓力轉移到對Redis上。
- 如果鍵不存在(即已過期),則返回訂單已取消的提示,釋放庫存。
- 利用訊息佇列的延時訊息:
- 使用如RabbitMQ、Kafka等訊息佇列系統,它們支援傳送延時訊息。
- 當訂單生成時,傳送一條延時為30分鐘的訊息到訊息佇列。
- 消費者監聽訊息佇列,當接收到延時訊息時,檢查對應訂單是否已支付。
- 如果未支付,則執行取消訂單的邏輯。
- 時間輪演算法:
- 時間輪演算法是一種高效處理定時任務或延時任務的演算法。
- 設計一個時間輪,每個槽位代表一定的時間間隔(如1分鐘)。
- 當訂單生成時,將其放入對應的時間槽位中。
- 時間輪不斷轉動,當轉到某個槽位時,檢查其中的訂單是否已支付,未支付則取消。
在實現上述方案時,還需要考慮以下幾點:
- 併發控制:確保在檢查訂單狀態和執行取消操作時,訂單資料的一致性。
- 事務性:更新訂單狀態和執行取消操作應在一個事務中完成,確保操作的原子性。
- 通知機制:如果訂單被取消,可能需要通知使用者或相關係統。
- 日誌記錄:記錄訂單取消的原因和時間,方便後續審計和排查問題。