如何保證冪等性
一、背景:
分散式場景下,介面的開發大都需要保證冪等性。
冪等性:一個介面被呼叫,不管幾次,產生一樣的效果,一樣返回結果。
介面呼叫過程中,很可能因為網路等原因進行重試呼叫,如果不能保證冪等性,那就完犢子了。
例如:使用者支付的介面,使用者有可能連續點選支付,總不能扣好幾次錢吧。
二、場景:
1、前端重複提交:
使用者快速重複點選多次,造成後端生成多個內容重複的資料。
2、介面超時重試:
對於給第三方呼叫的介面,為了防止網路抖動或其他原因造成請求丟失,這樣的介面一般都會設計成超時重試多次。
HTTP,RPC等在超時的情況下會有重試機制。
3、訊息重複消費:
MQ訊息中介軟體,訊息重複消費。
三、冪等性方案:
冪等性的保證,很明顯無法通過一個方案解決所有問題,只能具體場景具體分析的。
1、業務表唯一索引:
對於資料插入的場景來說,這是最常見的方案。
核心業務欄位設定為唯一索引,多次插入就會報錯,從而保證冪等性。
2、狀態流轉控制:
狀態流轉也是最常見的冪等性保證手段。
如配送業務中,騎手的操作肯定會對業務流轉狀態進行校驗。如果騎手已經提貨,那就肯定不能再次提貨的。
3、樂觀鎖版本號:
在業務表中新建一個欄位version,int型別。
服務A呼叫服務B需要更新的時候,需要提前查詢到version,然後作為引數傳過去。
如果資料更新的時候將version + 1,介面如果發生重試的時候,version已經發生變更,那麼也能保證冪等性。
PS:老實說,這個方案我本人沒用過,有點麻煩。大部分的更新不需要保證冪等性,最終的資料也能保證一致。
4、去重:
對於前端呼叫的介面,有些場景無法通過前面的方案保證冪等性。
介面中可以新增一個引數,這個引數保證每次請求都是唯一的。
然後將這個引數儲存,每次請求的入參校驗都會查詢這個引數是否存在,如果存在就返回。
引數儲存的方案可以是MySQL或者Redis。
對於本身併發較低的場景,不會對MySQL服務造成壓力,可以直接使用MySQL。否則,就要考慮Redis了,Redis這個key設定超時時間不用太長。
5、分散式鎖:
考慮到分散式環境下,很多方案的校驗如果無法保證序列的情況下,還是無法保證冪等性的。
例如,前面的狀態機校驗,併發環境下,可能還是會有問題,所以具體場景再進行分析。