再談冪等機制

AskHarries發表於2018-06-03

一、什麼是冪等性?

冪等性(Idempotence)。在HTTP/1.1規範中冪等性的定義是:

Methods can also have the property of “idempotence” in that (aside from error or expiration issues) the side-effects of N > 0 identical requests is the same as for a single request.

冪等性:就是使用者對於同一操作發起的一次請求或者多次請求的結果是一致的,不會因為多次點選而產生了副作用。

二、為什麼需要冪等

那麼我們為什麼需要介面具有冪等性呢?設想一下以下情形:

  • 在App中下訂單的時候,點選確認之後,沒反應,就又點選了幾次。在這種情況下,如果無法保證該介面的冪等性,那麼將會出現重複下單問題。
  • 在接收訊息的時候,訊息推送重複。如果處理訊息的介面無法保證冪等,那麼重複消費訊息產生的影響可能會非常大。

在分散式環境中,網路環境更加複雜,因前端操作抖動、網路故障、訊息重複、響應速度慢等原因,對介面的重複呼叫概率會比集中式環境下更大,尤其是重複訊息在分散式環境中很難避免。

三、如何保證介面的冪等性

介面的冪等性實際上就是介面可重複呼叫,在呼叫方多次呼叫的情況下,介面最終得到的結果是一致的。有些介面可以天然的實現冪等性,比如查詢介面,對於查詢來說,你查詢一次和兩次,對於系統來說,沒有任何影響,查出的結果也是一樣。

除了查詢功能具有天然的冪等性之外,增加、更新、刪除都要保證冪等性。那麼如何來保證冪等性呢?

3.1保證冪等策略

冪等需要通過唯一的業務單號來保證。也就是說相同的業務單號,認為是同一筆業務。使用這個唯一的業務單號來確保,後面多次的相同的業務單號的處理邏輯和執行效果是一致的。
下面以支付為例,在不考慮併發的情況下,實現冪等很簡單:①先查詢一下訂單是否已經支付過,②如果已經支付過,則返回支付成功;如果沒有支付,進行支付流程,修改訂單狀態為‘已支付’。

3.2防重複提交策略

上述的保證冪等方案是分成兩步的,第②步依賴第①步的查詢結果,無法保證原子性的。在高併發下就會出現下面的情況:第二次請求在第一次請求第②步訂單狀態還沒有修改為‘已支付狀態’的情況下到來。既然得出了這個結論,餘下的問題也就變得簡單:把查詢和變更狀態操作加鎖,將並行操作改為序列操作。

列舉三種改進方式:
1、悲觀鎖,select for update,整個執行過程中鎖定該訂單對應的記錄。
2、樂觀鎖,affectrows = db.update(“update payorder set state=’已支付’ where orderid=$orderid and state=’未支付’ “),如果affectrows=1,執行充值,否則返回已處理。
3、定義防重複表,orderid為unique key或者primary key,執行前,先insert,若insert成功則執行充值,否則返回已處理

四、總結

業務層設計協議時,要求請求方定義不重複的業務流水號。應用實現時,利用資料庫樂觀鎖、插入unique key的日誌等方式保證併發時的冪等。
冪等性把關環節,在協議設計評審中,評審重要業務RPC或者http介面是否支援冪等,程式碼評審中,重點把關請求併發時,是否仍舊能夠保證冪等性。設計人員和具體實現人員在實現過程中,也應該時刻自審冪等性的實現是否過關。
最後介紹一下美團點評開發GTI(它是一個輕量的重複操作關卡系統,它能夠確保在分散式環境中操作的唯一性。我們可以用它來間接保證每個操作的冪等性),感興趣可以去研究一下,20161008112749906

再談冪等機制


相關文章