1. 程式人生 > >分布式架構:並發重復請求和冪等場景技術實現總結

分布式架構:並發重復請求和冪等場景技術實現總結

gin 請求參數 數據 限制 沒有 超時 http 發的 說明

概念
重復請求是指一個請求因為某些原因被多次提交,場景簡述如下:
1)用戶快速多次點擊按鈕
2)Nginx失敗重試機制
3)服務框架失敗重試機制
4)MQ消息重復消費
5)第三方支付支付成功後,因為異常原因導致的多次異步回調;

冪等性是指同樣的請求參數,多次請求返回的結果相同。一般是因為重復請求導致的重復操作等,但重復請求不只包含並發時的重復請求還包括並並發情況下的業務重試。

基本原理
實現冪等需要兩個條件1、同一請求參數(並發請求或非並發請求);2、多次請求返回的結果一致。一般大家講的都是並發情況下的,使用並發控制解決,但還有一點是要滿足返回的結果一致,這個一般根據場景來定,是返回相同結果還是返回失敗。

發生原因
1)分布式系統中網絡的三態性:成功,失敗,未知,未知時一般三方系統會定期重試。
2)用戶重復提交或系統重試機制導致的多次請求;

常見解決方案
解決思路:並發控制+返回相同結果
分類:按是否更改數據可以把接口分為查詢類接口和更新類接口,查詢類接口天然支持冪等,因此冪等性主要是解決更新類接口冪等。
1)唯一索引
描述:比如訂單號做唯一索引,同一訂單號只能插入一條記錄;
應用場景:適用於單庫單表的新增場景。
2)Select+[Insert/Update]
描述:先進行查詢,根據查詢結果判斷是否符合更新條件,符合則更新;
應用場景:因為兩條Sql非原子操作,適合並發量不高的新增或修改場景。
3)數據庫樂觀鎖

描述:根據某一字段做為更新條件,如何不滿足,則更新失敗,比如狀態字段或增加自增版本號字段。【版本號字段可以解決ABA問題】
應用場景:適合非高並發的更新,且有版本控制字段的場景。如果高並發更新,評估利弊後可使用悲觀鎖。
4)防重Token
描述:頁面加載時,先請求服務端返回防重Token,用戶提交時將token一起提交到服務端,服務端判斷token是否存在,存在則執行,不存在則異常處理。【可根據業務規則是更新token的狀態值還是直接刪除token來標識已處理過】
應用場景:適用於沒有唯一性字段的添加或修改類場景。
5)防重表
描述:基於數據庫的方式進行並發控制,此表通過唯一字段+唯一索引來保障不重復處理數據。
應用場景:簡單分布式情況下對添加或修改類場景,進行並發或防重控制(也適用於老系統不想新增並發控制字段,統一進行並發字段存儲的場景)。【復雜分布式因為請求量或數據量太大,超過了單表的限制,此時防重表可能存在出錯的情況】
6)分布式鎖
描述:以唯一字段作為key進行加鎖,請求處理時先判斷是否有鎖,無鎖則先加鎖再處理邏輯,重復請求因為已經加鎖,則說明重復,則不處理。
場景:適合分布式高並發場景或不適用其它方式的場景,比如發驗證短信60秒控制,因為控制信息是記錄在緩存中的,無法使用樂觀鎖等方式,因此只能使用分布式鎖。

小結:解決方案的核心是根據資源(數據)的唯一性或唯一條件進行並發控制。

應用場景舉例
以訂單流程為例,介紹下冪等實現的常規解決方案。
訂單流程:

技術分享圖片

1、用戶提交訂單->待支付
2、用戶付款成功->待出庫
3、商品出庫->等待收貨
4、買家收貨->完成

其中:提交訂單為添加類接口,付款成功,商品出庫,等待收貨,完成為修改類接口。

1)訂單提交->待支付
單機環境:訂單號唯一索引或Select+Insert
分布式環境:Redis分布式鎖或防重token或防重表
2)用戶付款成功->待出庫,商品出庫-等待收貨,買家收貨->完成
單機環境:樂觀鎖
分布式環境:Redis鎖或防重token或防重表或Select+Update或樂觀鎖

降級方案
分布式鎖+唯一索引或樂觀鎖
分布式環境下,1)如果只加分布式鎖可能會存在鎖失效的情況,2)業務層鎖控制後,數據操作服務可能會超時重試;因此,依舊需要有唯一索引或數據庫樂觀鎖來進行並發控制,保障最後一道防線。

文章小結
本文對重復請求和需要冪等控制的場景進行了介紹,並講解了常見的解決方案,最後以訂單流程為例介紹了方案的具體應用。希望對大家在防重和冪等控制設計上有幫助,不足之處,請批評指正,歡迎一起交流討論。

分布式架構:並發重復請求和冪等場景技術實現總結