一、背景:
分布式場景下,接口的開發大都需要保證冪等性。
冪等性:一個接口被調用,不管幾次,產生一樣的效果,一樣返回結果。
接口調用過程中,很可能因為網絡等原因進行重試調用,如果不能保證冪等性,那就完犢子了。
例如:用戶支付的接口,用戶有可能連續點擊支付,總不能扣好幾次錢吧。
二、場景:
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、分布式鎖:
考慮到分布式環境下,很多方案的校驗如果無法保證串行的情況下,還是無法保證冪等性的。
例如,前面的狀態機校驗,並發環境下,可能還是會有問題,所以具體場景再進行分析。