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。
分布式系統接口的冪等性問題: