冪等策略分析

来源:http://www.cnblogs.com/tingyuxuan007/archive/2016/12/03/6128425.html
-Advertisement-
Play Games

冪等概念來自數學,表示N次變換和1次變換的結果是相同的。這裡討論在某些場景下,客戶端在調用服務沒有達到預期結果時,會進行多次調用,為避免多次重覆的調用對服務資源產生副作用,服務提供者會承諾滿足冪等。 ...



冪等概念來自數學,表示N次變換和1次變換的結果是相同的。這裡討論在某些場景下,客戶端在調用服務沒有達到預期結果時,會進行多次調用,為避免多次重覆的調用對服務資源產生副作用,服務提供者會承諾滿足冪等。

舉個慄子,雙十一零點剛過,小明就迫不及待地點擊提交訂單按鈕,選擇線上支付,點了確認支付按鈕,這時候網路有些慢,小明擔心心愛的商品被搶購一空,就點了多次確認付款按鈕,如果這個訂單扣款多次,客服熱線估計會被小明打爆。

什麼是冪等性

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.

這裡需要關註幾個重點:

  1. 冪等不僅僅只是一次(或多次)請求對資源沒有副作用(比如查詢資料庫操作,沒有增刪改,因此沒有對資料庫有任何影響)。
  2. 冪等還包括第一次請求的時候對資源產生了副作用,但是以後的多次請求都不會再對資源產生副作用。
  3. 冪等關註的是以後的多次請求是否對資源產生的副作用,而不關註結果。
  4. 網路超時等問題,不是冪等的討論範圍。

冪等性是系統服務對外一種承諾(而不是實現),承諾只要調用介面成功,外部多次調用對系統的影響是一致的。聲明為冪等的服務會認為外部調用失敗是常態,並且失敗之後必然會有重試。

什麼情況下需要冪等

業務開發中,經常會遇到重覆提交的情況,無論是由於網路問題無法收到請求結果而重新發起請求,或是前端的操作抖動而造成重覆提交情況。
在交易系統,支付系統這種重覆提交造成的問題有尤其明顯,比如:

  1. 用戶在APP上連續點擊了多次提交訂單,後臺應該只產生一個訂單;
  2. 向支付寶發起支付請求,由於網路問題或系統BUG重發,支付寶應該只扣一次錢。
    很顯然,聲明冪等的服務認為,外部調用者會存在多次調用的情況,為了防止外部多次調用對系統數據狀態的發生多次改變,將服務設計成冪等。

冪等VS防重

上面例子中小明遇到的問題,只是重覆提交的情況,和服務冪等的初衷是不同的。重覆提交是在第一次請求已經成功的情況下,人為的進行多次操作,導致不滿足冪等要求的服務多次改變狀態。而冪等更多使用的情況是第一次請求不知道結果(比如超時)或者失敗的異常情況下,發起多次請求,目的是多次確認第一次請求成功,卻不會因多次請求而出現多次的狀態變化。

什麼情況下需要保證冪等性

以SQL為例,有下麵三種場景,只有第三種場景需要開發人員使用其他策略保證冪等性:

  1. SELECT col1 FROM tab1 WHER col2=2,無論執行多少次都不會改變狀態,是天然的冪等。
  2. UPDATE tab1 SET col1=1 WHERE col2=2,無論執行成功多少次狀態都是一致的,因此也是冪等操作。
  3. UPDATE tab1 SET col1=col1+1 WHERE col2=2,每次執行的結果都會發生變化,這種不是冪等的。

為什麼要設計冪等性的服務

冪等可以使得客戶端邏輯處理變得簡單,但是卻以服務邏輯變得複雜為代價。滿足冪等服務的需要在邏輯中至少包含兩點:

  1. 首先去查詢上一次的執行狀態,如果沒有則認為是第一次請求
  2. 在服務改變狀態的業務邏輯前,保證防重覆提交的邏輯

冪等的不足

冪等是為了簡化客戶端邏輯處理,卻增加了服務提供者的邏輯和成本,是否有必要,需要根據具體場景具體分析,因此除了業務上的特殊要求外,儘量不提供冪等的介面。

  1. 增加了額外控制冪等的業務邏輯,複雜化了業務功能;
  2. 把並行執行的功能改為串列執行,降低了執行效率。

保證冪等策略

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

防重覆提交策略

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

樂觀鎖

如果只是更新已有的數據,沒有必要對業務進行加鎖,設計表結構時使用樂觀鎖,一般通過version來做樂觀鎖,這樣既能保證執行效率,又能保證冪等。例如:
UPDATE tab1 SET col1=1,version=version+1 WHERE version=#version#
不過,樂觀鎖存在失效的情況,就是常說的ABA問題,不過如果version版本一直是自增的就不會出現ABA的情況。(從網上找了一張圖片很能說明樂觀鎖,引用過來,出自Mybatis對樂觀鎖的支持

防重表

使用訂單號orderNo做為去重表的唯一索引,每次請求都根據訂單號向去重表中插入一條數據。第一次請求查詢訂單支付狀態,當然訂單沒有支付,進行支付操作,無論成功與否,執行完後更新訂單狀態為成功或失敗,刪除去重表中的數據。後續的訂單因為表中唯一索引而插入失敗,則返回操作失敗,直到第一次的請求完成(成功或失敗)。可以看出防重表作用是加鎖的功能。

分散式鎖

這裡使用的防重表可以使用分散式鎖代替,比如Redis。訂單發起支付請求,支付系統會去Redis緩存中查詢是否存在該訂單號的Key,如果不存在,則向Redis增加Key為訂單號。查詢訂單支付已經支付,如果沒有則進行支付,支付完成後刪除該訂單號的Key。通過Redis做到了分散式鎖,只有這次訂單訂單支付請求完成,下次請求才能進來。相比去重表,將放併發做到了緩存中,較為高效。思路相同,同一時間只能完成一次支付請求

token令牌

這種方式分成兩個階段:申請token階段和支付階段。
第一階段,在進入到提交訂單頁面之前,需要訂單系統根據用戶信息向支付系統發起一次申請token的請求,支付系統將token保存到Redis緩存中,為第二階段支付使用。
第二階段,訂單系統拿著申請到的token發起支付請求,支付系統會檢查Redis中是否存在該token,如果存在,表示第一次發起支付請求,刪除緩存中token後開始支付邏輯處理;如果緩存中不存在,表示非法請求。
實際上這裡的token是一個信物,支付系統根據token確認,你是你媽的孩子。不足是需要系統間交互兩次,流程較上述方法複雜。

支付緩衝區

把訂單的支付請求都快速地接下來,一個快速接單的緩衝管道。後續使用非同步任務處理管道中的數據,過濾掉重覆的待支付訂單。優點是同步轉非同步,高吞吐。不足是不能及時地返回支付結果,需要後續監聽支付結果的非同步返回。

我是葛一凡,希望對你有幫助。
微信公眾號微信公眾號

參考

    1. 高併發的核心技術-冪等的實現方案
    2. 防重覆請求處理的實踐與總結
    3. 分散式服務協調—冪等(Idempotent)機制
    4. 分散式系統介面冪等性
    5. 冪等性 個人理解及應用
    6. 編程中的冪等性 —— HTTP冪等性
    7. 系統冪等以及常用實現方式
    8. 高併發系統數據冪等的技術嘗試
    9. Mybatis對樂觀鎖的支持

您的分享是我們最大的動力!

-Advertisement-
Play Games
更多相關文章
  • 反骨仔的 2016 年度全文目錄索引 目錄 Linq 系列 非同步編程系列(一) 非同步編程系列(二) 委托與事件系列 C# 基礎回顧 .NET 相關 SQL 基礎梳理 其它 - 暫無分類 Linq 系列 走進 LINQ 的世界(推薦) 進階 - LINQ 標準查詢操作概述(強烈推薦) Linq To ...
  • 工欲善其事,必先利其器。如今 Web 開發標準越來越高,Web 開發者也在不斷尋找途徑提升自己的技能。為使大家的開發工作更順利進行,本文整理了 10+ 款比較優秀的 Web 開發工具,希望對你有幫助。 喜歡的不要忘了收藏、點贊和打賞哦,感謝大家的支持! Bootstrap Bootstrap 是快速 ...
  • 英文文檔: The constructor builds a tuple whose items are the same and in the same order as iterable‘s items. iterable may be either a sequence, a containe ...
  • 1、直接把表單的參數寫在Controller相應的方法的形參中,適用於get方式提交,不適用於post方式提交。若"Content-Type"="application/x-www-form-urlencoded",可用post提交 url形式:http://localhost:8080/SSMDe ...
  • 為瞭解決C++記憶體泄漏的問題,C++11引入了智能指針(Smart Pointer)。 智能指針的原理是,接受一個申請好的記憶體地址,構造一個保存在棧上的智能指針對象,當程式退出棧的作用域範圍後,由於棧上的變數自動被銷毀,智能指針內部保存的記憶體也就被釋放掉了,除非將智能指針保存起來。 C++11提供了 ...
  • JDBC2.0提供了javax.sql.DataSource介面,它負責建立與資料庫的連接,在應用程式中訪問資料庫時不必編寫連接資料庫的代碼,可以直接從數據源獲得資料庫連接 1.資料庫和連接池 在DataSource中事先建立了多個資料庫連接,這些資料庫連接保存在連接池(ConnectPool)中。 ...
  • ORM 眾所周知有很多不同的資料庫系統,並且其中的大部分系統都包含Python介面,能夠讓我們更好的利用它們的功能,而這些系統唯一的缺點就是需要你瞭解SQL,如果你是一個更願意操縱Python對象,而不是SQL查詢的程式員,並且仍然希望使用關係資料庫作為你的數據後端,那麼我們可以使用ORM。 這些O ...
  • 1、流程分析: 1. 請求發送給 StrutsPrepareAndExecuteFilter 2. StrutsPrepareAndExecuteFilter 詢問 ActionMapper: 該請求是否是一個 Struts2 請求(即是否返回一個非空的 ActionMapping 對象) 3. 若 ...
一周排行
    -Advertisement-
    Play Games
  • 移動開發(一):使用.NET MAUI開發第一個安卓APP 對於工作多年的C#程式員來說,近來想嘗試開發一款安卓APP,考慮了很久最終選擇使用.NET MAUI這個微軟官方的框架來嘗試體驗開發安卓APP,畢竟是使用Visual Studio開發工具,使用起來也比較的順手,結合微軟官方的教程進行了安卓 ...
  • 前言 QuestPDF 是一個開源 .NET 庫,用於生成 PDF 文檔。使用了C# Fluent API方式可簡化開發、減少錯誤並提高工作效率。利用它可以輕鬆生成 PDF 報告、發票、導出文件等。 項目介紹 QuestPDF 是一個革命性的開源 .NET 庫,它徹底改變了我們生成 PDF 文檔的方 ...
  • 項目地址 項目後端地址: https://github.com/ZyPLJ/ZYTteeHole 項目前端頁面地址: ZyPLJ/TreeHoleVue (github.com) https://github.com/ZyPLJ/TreeHoleVue 目前項目測試訪問地址: http://tree ...
  • 話不多說,直接開乾 一.下載 1.官方鏈接下載: https://www.microsoft.com/zh-cn/sql-server/sql-server-downloads 2.在下載目錄中找到下麵這個小的安裝包 SQL2022-SSEI-Dev.exe,運行開始下載SQL server; 二. ...
  • 前言 隨著物聯網(IoT)技術的迅猛發展,MQTT(消息隊列遙測傳輸)協議憑藉其輕量級和高效性,已成為眾多物聯網應用的首選通信標準。 MQTTnet 作為一個高性能的 .NET 開源庫,為 .NET 平臺上的 MQTT 客戶端與伺服器開發提供了強大的支持。 本文將全面介紹 MQTTnet 的核心功能 ...
  • Serilog支持多種接收器用於日誌存儲,增強器用於添加屬性,LogContext管理動態屬性,支持多種輸出格式包括純文本、JSON及ExpressionTemplate。還提供了自定義格式化選項,適用於不同需求。 ...
  • 目錄簡介獲取 HTML 文檔解析 HTML 文檔測試參考文章 簡介 動態內容網站使用 JavaScript 腳本動態檢索和渲染數據,爬取信息時需要模擬瀏覽器行為,否則獲取到的源碼基本是空的。 本文使用的爬取步驟如下: 使用 Selenium 獲取渲染後的 HTML 文檔 使用 HtmlAgility ...
  • 1.前言 什麼是熱更新 游戲或者軟體更新時,無需重新下載客戶端進行安裝,而是在應用程式啟動的情況下,在內部進行資源或者代碼更新 Unity目前常用熱更新解決方案 HybridCLR,Xlua,ILRuntime等 Unity目前常用資源管理解決方案 AssetBundles,Addressable, ...
  • 本文章主要是在C# ASP.NET Core Web API框架實現向手機發送驗證碼簡訊功能。這裡我選擇是一個互億無線簡訊驗證碼平臺,其實像阿裡雲,騰訊雲上面也可以。 首先我們先去 互億無線 https://www.ihuyi.com/api/sms.html 去註冊一個賬號 註冊完成賬號後,它會送 ...
  • 通過以下方式可以高效,並保證數據同步的可靠性 1.API設計 使用RESTful設計,確保API端點明確,並使用適當的HTTP方法(如POST用於創建,PUT用於更新)。 設計清晰的請求和響應模型,以確保客戶端能夠理解預期格式。 2.數據驗證 在伺服器端進行嚴格的數據驗證,確保接收到的數據符合預期格 ...