一.什么是接口冪等性?
一個冪等操作的特點是其任意多次執行所產生的影響均與一次執行的影響相同。冪等函數或冪等方法是指可以使用相同參數重復執行,並能獲得相同結果的函數/方法。這些函數/方法不會影響系統狀態,因此不用擔心重復執行會對系統造成改變。
個人理解,從后端的角度通俗來說就是:
對於一個請求,在參數相同的情況下,請求一次或者請求多次,響應結果都是一致的;
二.為什么需要接口冪等性的支持?
1.前端重復提交數據的情況下,后端之應該返回相同的數據結果或只對該次提交產生一個反應結果;
2.手機支付的場景下,發起支付時,如遇到網絡重發或者系統BUG重發(例如網絡延遲導致 Feign重試的情況)應該只扣一次款;
3.消息推送的場景下,消息推送只發一次;
4.用戶下單創建業務訂單的場景下,一次請求只能創建一個,而不能因為網絡或系統問題多次創建;
三.常見的接口冪等性設計方案(以Http協議的接口為例)
1.查詢操作(GET請求)
由於GET請求具有天然的冪等性,因此查詢一次和查詢多次都是冪等的
2.刪除操作(DELETE請求)
在參數相同的情況下,刪除一次和多次刪除的結果都是一致的,結果都是將該數據刪除
3.數據庫唯一索引,新增數據時防止增加臟數據
適用場景:
例如在創建賬號時,一個手機號只能創建一個賬號,因此對於可能操作問題出現的短時間的重復創建問題,除了代碼中的邏輯校驗之外,使用數據庫唯一索引是比較適用的方式,可以用來防止新增數據存在臟數據;
優化:
並發新增數據時,對於數據添加失敗的情況下,可以捕獲異常,在catch代碼塊中再根據該唯一索引字段查詢一邊數據,返回與正常數據添加的相同的結果,從而友好的提示前端數據;
4.唯一token機制,防止頁面重復提交
業務要求:
頁面的數據只能被點擊提交一次
發生原因:由於重復點擊或者網絡重發,或者nginx重發等情況會導致數據被重復提交
解決辦法:集群環境:采用token加redis(redis單線程的,處理需要排隊) 單JVM環境:采用token加redis或token加jvm內存
處理流程:
-
-
-
數據提交前要向服務的申請token,token放到redis或jvm內存,token有效時間
-
提交后后台校驗token,同時刪除token,生成新的token返回
-
-
token特點:
要申請,一次有效性,可以限流
注意:redis要用刪除操作來判斷token,刪除成功代表token校驗通過,如果用select+delete來校驗token,存在並發問題,不建議使用
5.悲觀鎖
獲取數據的時候加鎖獲取
select * from table_xxx where id='xxx' for update;
注意:id字段一定是主鍵或者唯一索引(這樣只鎖定索引對應的那一行),不然會鎖表
悲觀鎖使用時一般伴隨事務一起使用,數據鎖定時間可能會很長,根據實際情況選用
6.樂觀鎖
樂觀鎖只是在更新數據那一刻鎖表,其他時間不鎖表,所以相對於悲觀鎖,效率更高。
樂觀鎖的實現方式多種多樣可以通過version或者其他狀態條件:
1.通過版本號實現:(版本號需要提前返回給前端)
update table_xxx set name=#name#,version=version+1 where version=#version#
2.通過條件限制
update tablexxx set avaiamount=avaiamount-#subAmount# where avaiamount-#subAmount# >= 0
要求:quality-#subQuality# >= ,這個情景適合不用版本號,只更新是做數據安全校驗,適合庫存模型,扣份額和回滾份額,性能更高
注意:樂觀鎖的更新操作,最好用主鍵或者唯一索引來更新,這樣是行鎖,否則更新時會鎖表,上面兩個sql改成下面的兩個更好
update tablexxx set name=#name#,version=version+1 where id=#id# and version=#version#update tablexxx set avaiamount=avaiamount-#subAmount# where id=#id# and avai_amount-#subAmount# >= 0
7.對外提供接口的api如何保證冪等
如銀聯提供的付款接口:需要接入商戶提交付款請求時附帶:source來源,seq序列號
source+seq在數據庫里面做唯一索引,防止多次付款,(並發時,只能處理一個請求)
重點 對外提供接口為了支持冪等調用,接口有兩個字段必須傳,一個是來源source,一個是來源方序列號seq,這個兩個字段在提供方系統里面做聯合唯一索引
這樣當第三方調用時,先在本方系統里面查詢一下,是否已經處理過,返回相應處理結果;沒有處理過,進行相應處理,返回結果。
注意,為了冪等友好,一定要先查詢一下,是否處理過該筆業務,不查詢直接插入業務系統,會報錯,但實際已經處理了。