突破Java面試(38)-分佈式服務接口的冪等性

1 面試題

分佈式服務接口的冪等性如何設計(比如不能重複扣款)?

2 考點分析

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

一個分佈式系統中的某個接口,要保證冪等性,如何保證?
這個事,其實是你做分佈式系統的時候必須要考慮的一個生產環境的技術問題.爲什麼呢?

假如你有個服務提供一個接口,這服務部署在5臺機器上,有個付款接口.
然後用戶在前端操作時,不知爲啥,一個訂單不小心發起了兩次支付請求,然後這倆請求分散在了這個服務部署的不同的機器上,這下好了,結果一個訂單扣款扣兩次,尷尬了!

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

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

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

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

3 冪等性

所謂冪等性,簡而言之,就是一個接口,多次發起同一個請求,接口得保證結果是準確的,比如不能多扣款,不能多插入一條數據,不能將統計值多加了1.

保證冪等性主要有如下幾點

  • 對於每個請求必須有一個唯一的標識
    舉個例子:訂單支付請求,肯定得包含訂單id,一個訂單id最多支付一次
  • 每次處理完請求後,須有一個記錄標識該請求已被處理
    比如說常見的方案是在MySQL中記錄個狀態字段

比如支付之前記錄一條這個訂單的支付流水

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

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

要求是支付一個訂單,必須插入一條支付流水,order_id建立一個唯一鍵unique key
你在支付一個訂單前,先插入一條支付流水,order_id就已經傳過去了
你就可以寫一個標識到Redis中,set order_id payed,當重複請求過來時,先查Redis的order_id對應的value,若爲payed說明已支付,就別重複支付了!

然後呢,你再重複支付這個訂單的時候,你寫嘗試插入一條支付流水,數據庫給你報錯了,說unique key衝突了,整個事務回滾就可以了
來保存一個是否處理過的標識也可以,服務的不同實例可以一起操作Redis.

參考

  • 《Java工程師面試突擊第1季-中華石杉老師》

Github

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