分佈式架構:併發重複請求和冪等場景技術實現總結

概念

重複請求是指一個請求因為某些原因被多次提交,場景簡述如下:

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)業務層鎖控制後,數據操作服務可能會超時重試;因此,依舊需要有唯一索引或數據庫樂觀鎖來進行併發控制,保障最後一道防線。


分享到:


相關文章: