1. 程式人生 > >34、分散式服務介面的冪等性如何設計(比如不能重複扣款)?

34、分散式服務介面的冪等性如何設計(比如不能重複扣款)?

1、面試題

分散式服務介面的冪等性如何設計(比如不能重複扣款)?

2、面試官心裡分析

從這個問題開始,面試官就已經進入了實際的生產問題的面試了。

一個分散式系統中的某個介面,要保證冪等性,該如何保證?這個事兒其實是你做分散式系統的時候必須要考慮的一個生產環境的技術問題。啥意思呢?

你看,假如你有個服務提供一個介面,結果這服務部署在了5臺機器上,接著有個介面就是付款介面。然後人家使用者在前端上操作的時候,不知道為啥,總之就是一個訂單不小心發起了兩次支付請求,然後這倆請求分散在了這個服務部署的不同的機器上,好了,結果一個訂單扣款扣兩次?尷尬了。

或者是訂單系統呼叫支付系統進行支付,結果不小心因為網路超時了,然後訂單系統走了前面我們看到的那個重試機制,咔嚓給你重試了一把,好,支付系統收到一個支付請求兩次,而且因為負載均衡演算法落在了不同的機器上,尷尬了。

所以你肯定得知道這事兒,否則你做出來的分散式系統恐怕容易埋坑。

網路問題很常見,100次請求,都ok;1萬次,可能1次是超時會重試;10萬,10次;100萬,100次;如果有100個請求重複了,你沒處理,導致訂單扣款2次,100個訂單都扣錯了;每天被100個使用者投訴;一個月被3000個使用者投訴。

我們之前生產就遇到過,是往資料庫裡寫入資料,重複的請求,就導致我們的資料經常會錯,出現一些重複資料,就會導致一些問題。

3、面試題剖析

這個不是技術問題,這個沒有通用的一個方法,這個是結合業務來看應該如何保證冪等性的,你的經驗。

所謂冪等性,就是說一個介面,多次發起同一個請求,你這個介面得保證結果是準確的,比如不能多扣款,不能多插入一條資料,不能將統計值多加了1。這就是冪等性,不給大家來學術性詞語了。

其實保證冪等性主要是三點:

(1)對於每個請求必須有一個唯一的標識,舉個例子:訂單支付請求,肯定得包含訂單id,一個訂單id最多支付一次,對吧!

(2)每次處理完請求之後,必須有一個記錄標識這個請求處理過了,比如說常見的方案是在mysql中記錄個狀態啥的,比如支付之前記錄一條這個訂單的支付流水,而且支付流水採。

(3)每次接收請求需要進行判斷之前是否處理過的邏輯處理,比如說,如果有一個訂單已經支付了,就已經有了一條支付流水,那麼如果重複傳送這個請求,則此時先插入支付流水,orderId已經存在了,唯一鍵約束生效,報錯插入不進去的。然後你就不用再扣款了。

(4)上面只是給大家舉個例子,實際運作過程中,你要結合自己的業務來,比如說用redis用orderId作為唯一鍵。只有成功插入這個支付流水,才可以執行實際的支付扣款。

要求是支付一個訂單,必須插入一條支付流水,order_id建一個唯一鍵,unique key

所以你在支付一個訂單之前,先插入一條支付流水,order_id就已經進去了。

你就可以寫一個標識到redis裡面去,set order_id payed,下一次重複請求過來了,先查redis的order_id對應的value,如果是payed就說明已經支付過了,你就別重複支付了。

然後呢,你再重複支付這個訂單的時候,你寫嘗試插入一條支付流水,資料庫給你報錯了,說unique key衝突了,整個事務回滾就可以了。

來儲存一個是否處理過的標識也可以,服務的不同例項可以一起操作redis。

分散式系統介面的冪等性問題.png

文集:https://www.jianshu.com/nb/32293473