博客 / 詳情

返回

如何保證冪等性

如何保證冪等性

什麼是冪等性

冪等性:概念源於數學,原意是指一個操作如果連續執行多次所產生的結果與僅執行一次的效果相同,那麼我們就稱這個操作是冪等的。

調用方,對一個系統進行重複調用(參數全部相同),不論重複調用多少次,這些調用對系統的影響都是相同的效果。不會隨着次數的變化而改變。

舉例:

  1. 冪等性

假設對象 Person中 有個 name 屬性,有個 setName 方法

setName(String name){
   this.name = name
}

那這個方法就是天然冪等的,你輸入相同的參數,不論你重複調用多少次都是將名字設置為“小明”,其對對象Person的影響都是一樣的。這就是天然冪等性。

  1. 非冪等性

還是拿對象 Person 舉例子,假設對象中有個age屬性,有個increaseAge 方法

increaseAge(){
   this.age++;
}

我們按正常的步驟一次一次調用是不會有問題的,如果調用者沒有控制好邏輯,一次流程重複調用好幾次,這時候影響效果和一次是有非常大區別,代碼編寫者以為它只會調用一次,結果出現了意外調用了很多次,恰好方法不具有冪等性,於是就會出現和預期不一樣的效果。

這個方法本身是不具備冪等性的,我們可以修改這個方法,讓其傳入一個標識符,每一次重複的請求會有相同的標識符,方法內部可以根據標識符查數據庫是不是已經處理過,如果處理過就不重複處理。這樣方法就具備了冪等性。

  1. 作用

比如我們常常遇到的訂單創建,支付等業務。

  • 如果一個“創建訂單”接口實現了冪等性,當收到兩次同樣的創建請求時,系統應該要麼拒絕第二個請求(因為它已經是重複請求),要麼確保只有一個訂單被創建,而不是兩個完全一樣的訂單。
  • 對於一個“支付”接口,冪等性要求即便用户由於網絡原因反覆點擊支付按鈕,服務端也只會扣除用户賬户一次金額,避免重複扣費。

導致冪等性問題的原因

  1. 網絡波動不穩定
    網絡通信中的丟包、延遲等情況可能導致客户端未收到服務端的響應或服務端未收到客户端的請求,此時客户端可能會重試發送請求,導致接口被重複調用。
  2. 用户操作
    用户快速重複點擊導致

    用户在等待響應時,由於不確定是否操作成功,可能會多次點擊提交按鈕,進而發送多次相同的請求。

    用户頻繁刷新頁面

    尤其是在某些提交操作尚未完成時,刷新頁面可能會重新發送請求。

    用户在瀏覽器上點擊回退然後再重複之間的提交操作,

    這都可能會導致重新發送請求。
  3. 重試機制
    在高可用性設計中,客户端常常設置有重試機制,當請求失敗或超時時會自動重新發起請求。而在分佈式系統中,服務間調用也可能有重試策略,以應對臨時故障。

    比如 Nginx 重試,RPC 重試,或者調用方業務層中進行重試。
  4. 定時任務或異步處理
    在定時任務中,如果定時任務調度或邏輯設計不當,可能會導致同一任務被執行多次。

    在消息隊列中,消息可能會因為異常等原因被重複消費。

  5. 併發控制
    缺乏有效的併發控制手段,導致在併發環境下,針對同一資源的操作被多次執行。

如何保證冪等性

總的來説,導致冪等性問題可以粗略的歸類於兩種情況:前端調用以及服務端調用

服務端控制

在服務端接口處理邏輯時,可以通過通過一些特定的標識符或請求參數來校驗請求的冪等性,以確保同樣的請求不會被重複處理。
  1. 唯一標識符

客户端每次發起請求會攜帶一個全局唯一的標識符。服務器接收到請求後就會對這個標識符進行檢查,

若服務器發現該標識符已經在系統中存在,表明這是一個重複請求,此時服務器可以選擇忽略該請求,或者向客户端返回已處理過相同請求的結果信息。

若服務器未找到該標識符存在於系統內,則認定該請求為新請求,服務器將繼續對其進行正常處理,並將此唯一標識符保存至系統中,以便於後續對接收的請求進行有效性校驗,防止同一請求的重複處理。

比如我們在要求上游 ERP 系統對接訂單平台時就會要求上游傳遞一個賬號下全局唯一的一個參考單號,這個參考單號一個很重要的作用就是保證接口冪等性。
  1. 請求參數

某些請求參數確實可以用來輔助校驗請求的冪等性。

例如,時間戳可以作為一種可能的請求參數,在處理請求時,服務器可以通過比較時間戳與服務器當前時間來判斷請求的有效性。若時間戳與當前時間之間的差異超出預設的合理範圍(如幾秒鐘到幾分鐘不等,具體閾值視業務場景而定),服務器可以推測該請求可能是由於網絡延遲或者其他原因導致的重複提交。

單純依靠時間戳來判斷冪等性和重複請求並不完全準確,因為不同的客户端時間可能並不精確同步,而且時間戳本身無法保證全局唯一性。但是它可以作為一種有效的輔助手段來減少重複處理的可能性
  1. 狀態機設計

對於狀態轉移類的操作類型的業務,可採用狀態機設計,每次請求只允許合法的狀態變遷,非法狀態變遷(如已經完成的訂單不允許再次支付)將被拒絕。

  1. 樂觀鎖

在更新數據時,可以通過版本號時間戳等機制判斷數據是否已被修改,防止因併發請求導致的多次更新問題。具體做法:

  • 在數據庫表中增加一個版本號字段(version)或者時間戳字段(timestamp)。
  • 客户端第一次請求時獲取數據的版本號或時間戳。
  • 客户端發起更新操作時,將上次讀取的版本號或時間戳一起發送回服務器。
  • 服務器在執行更新操作前,首先檢查當前數據庫中的版本號或時間戳是否與客户端提交的一致。

    • 如果一致,説明在這期間數據沒有被其他事務修改過,於是更新數據並遞增版本號或更新時間戳。
    • 如果不一致,説明數據已經被修改過,此時服務器拒絕本次更新請求,返回錯誤提示,客户端可以根據錯誤信息決定是否重新獲取最新數據再嘗試更新。

通過這種方式,即使客户端因為網絡原因或其他因素導致同一請求被多次發送,樂觀鎖機制能確保只有在數據未被其他事務修改的前提下,才會執行更新操作,從而達到接口冪等的效果。

前端調用

實現冪等性方案示例

附錄

狀態機設計模式

是一種行為型設計模式,用於描述對象在不同狀態下的行為(在狀態機模式中,對象的行為取決於其內部狀態,並且在不同的狀態下,對象可能會有不同的行為)。

核心思想:是將對象的行為與其狀態解耦,使得狀態之間的轉換更加清晰和可控。狀態機模式通常涉及定義一組狀態以及狀態之間的轉換規則。


結構:

該模式主要包含以下幾個要素:

  1. 狀態(State):狀態機模式中的狀態表示對象所處的特定狀態。每個狀態都定義了對象在該狀態下的行為。
  2. 上下文(Context):上下文是包含狀態機的對象。它維護了當前狀態,並在狀態之間的轉換髮生時更新狀態。
  3. 轉換(Transition):轉換描述了對象從一個狀態轉移到另一個狀態的過程。它通常受到一些條件或觸發事件的影響。
  4. 動作(Action):動作是狀態轉換期間可能執行的操作或行為。

狀態機模式的核心思想是將對象的行為與其狀態解耦,從而使得狀態之間的轉換更加清晰和可控。它有助於簡化複雜系統的設計和實現,特別是當系統具有多個可能狀態和狀態之間的複雜轉換關係時。

狀態模式 | 菜鳥教程

如何保證接口冪等性 - 碼農Academy - 博客園

什麼是冪等性?如何解決冪等性問題?-CSDN博客

一口氣説出四種冪等性解決方案,面試官都驚呆了!! - 知乎


user avatar philadelphia 頭像
1 位用戶收藏了這個故事!

發佈 評論

Some HTML is okay.