微服務冪等性

2021-03-02 檸檬夕

1背景介紹1.1 冪等性定義

數學定義
在數學裡,冪等有兩種主要的定義:

HTTP規範定義
在HTTP/1.1規範中冪等性的定義是:

A request method is considered "idempotent" if the intended effect onthe server of multiple identical requests with that method is the same as the effect for a single such request. Of the request methods defined by this specification, PUT, DELETE, and safe request methods are idempotent.

        HTTP的冪等性指的是一次和多次請求某一個資源應該具有相同的副作用。如通過PUT接口將數據的Status置為1,無論是第一次執行還是多次執行,獲取到的結果應該是相同的,即執行完成之後Status =1。

1.2 冪等概念

        微服務架構中,冪等是一致性方面的一個重要概念。冪等(Idempotent)是一個數學領域與計算機學的概念,常見於抽象代數中。而在編程中,一個冪等操作的特點是指其任意多次執行所產生的影響均與一次執行的影響相同。

        有人會簡單的認為,直接禁止所有重試即可。然而,重試是降低微服務失敗率的重要手段。因為網絡波動、系統資源分配的不確定性、跨機房的請求等等原因,都會或多或少的導致一小部分請求的失敗。而這部分失敗的請求中,又有大部分請求其實只需要簡單重試幾次,即可成功。

1.3 重試機制

降低微服務失敗率

提高至四個或五個9

提高微服務架構的容錯性

提高微服務架構的高可靠性

2 冪等分析2.1 冪等場景

        可能會發生重複請求或消費的場景,在微服務架構中是隨處可見的。以下是筆者梳理的幾個常見場景:

網絡波動:因網絡波動,可能會引起重複請求

分布式消息消費:任務發布後,使用分布式消息服務來進行消費

用戶重複操作:用戶在使用產品時,可能會無意的觸發多筆交易,甚至沒有響應而有意觸發多筆交易

未關閉的重試機制:因開發人員、測試人員或運維人員沒有檢查出來,而開啟的重試機制(如Nginx重試、RPC通信重試或業務層重試等)

2.2 CRUD分析

新增類請求(C)

查詢類動作(R)

更新類請求(U)

基於主鍵的計算式Update,不具備冪等性,即:UPDATE goods SET number=number-1 WHERE id=1

基於主鍵的非計算式Update,具備冪等性,即:UPDATE goods SET number=newNumber WHERE id=1

基於條件查詢的Update,不一定具有冪等性(需要根據實際情況進行分析判斷)

刪除類請求(D)

2.3 冪等重要性

針對一個微服務架構,如果不支持冪等操作,那將會出現以下情況:

電商超賣現象

重複轉帳、扣款或付款

重複增加金幣、積分或優惠券

超賣現象
        比如某商品的庫存為1,此時用戶1和用戶2並發購買該商品,用戶1提交訂單後該商品的庫存被修改為0,而此時用戶2並不知道的情況下提交訂單,該商品的庫存再次被修改為-1這就是超賣現象。

        究其深層原因,是因為資料庫底層的寫操作和讀操作可以同時進行,雖然寫操作默認帶有隱式鎖(即對同一數據不能同時進行寫操作)但是讀操作默認是不帶鎖的,所以當用戶1去修改庫存的時候,用戶2依然可以都到庫存為1,所以出現了超賣現象。

        解決方案A:可以對讀操作加上顯式鎖(即在select …語句最後加上for update)這樣一來用戶1在進行讀操作時用戶2就需要排隊等待了。但問題來了,如果該商品很熱門並發量很高那麼效率就會大大的下降,如何解決呢?(解決方案B)

        解決方案B:我們可以有條件有選擇的在讀操作上加鎖,比如可以對庫存做一個判斷,當庫存小於一個量時開始加鎖,讓購買者排隊,這樣一來就解決了超賣現象。

3 何種接口提供冪等性3.1 HTTP冪等性

在HTTP規範中定義GET、PUT和DELETE方法應該具有冪等性,具體如下:

The GET method requests transfer of a current selected representatiofor the target resourceGET is the primary mechanism of information retrieval and the focus of almost all performance optimizations. Hence, when people speak of retrieving some identifiable information via HTTP, they are generally referring to making a GET request.

        GET方法是向伺服器查詢,不會對系統產生副作用,具有冪等性(不代表每次請求都是相同的結果)。

The PUT method requests that the state of the target resource be created or replaced with the state defined by the representation enclosed in the request message payload.

        也就是說PUT方法首先判斷系統中是否有相關的記錄,如果有記錄則更新該記錄,如果沒有則新增記錄。

The DELETE method requests that the origin server remove the association between the target resource and its current functionality. In effect, this method is similar to the rm command in UNIX: it expresses a deletion operation on the URI mapping of the origin server rather than an expectation that the previously associated information be deleted.

        DELETE方法是刪除伺服器上的相關記錄。

3.2 實際業務案例

        現在簡化為這樣一個系統,用戶購買商品的訂單系統與支付系統;訂單系統負責記錄用戶的購買記錄已經訂單的流轉狀態(orderStatus),支付系統用於付款,提供:

1
4boolean pay(int accountid,BigDecimal amount);

        訂單系統與支付系統通過分布式網絡交互描述如下:

訂單冪等性
        這種情況下,支付系統已經扣款,但是訂單系統因為網絡原因,沒有獲取到確切的結果,因此訂單系統需要重試。由上圖可見,支付系統並沒有做到接口的冪等性,訂單系統第一次調用和第二次調用,用戶分別被扣了兩次錢,不符合冪等性原則(同一個訂單,無論是調用了多少次,用戶都只會扣款一次)。如果需要支持冪等性,付款接口需要修改為以下接口:

1boolean pay(int orderId,int accountId,BigDecimal amount);

    通過orderId來標定訂單的唯一性,付款系統只要檢測到訂單已經支付過,則第二次調用不會扣款而會直接返回結果:

訂單支持冪等性
        在不同的業務中不同接口需要有不同的冪等性,特別是在分布式系統中,因為網絡原因而未能得到確定的結果,往往需要支持接口冪等性。3.3 分布式應用冪等性

        隨著分布式應用及微服務的普及,因為網絡原因而導致調用應用未能獲取到確切的結果從而導致重試,這就需要被調用應用具有冪等性。例如上文所闡述的支付系統,針對同一個訂單保證支付的冪等性,一旦訂單的支付狀態確定之後,以後的操作都會返回相同的結果,對用戶的扣款也只會有一次。這種接口的冪等性,簡化到數據層面的操作:

1update userAmount set amount = amount - 'value' ,paystatus = 'paid' where orderId= 'orderid' and paystatus = 'unpay'

        其中value是用戶要減少的訂單,paystatus代表支付狀態,paid代表已經支付,unpay代表未支付,orderid是訂單號。在上文中提到的訂單系統,訂單具有自己的狀態(orderStatus),訂單狀態存在一定的流轉。訂單首先有提交(0)→付款中(1)→付款成功(2)/ 付款失敗(3),簡化之後其流轉路徑如圖:

訂單狀態流轉的冪等性
    當orderStatus = 1 時,其前置狀態只能是0,也就是說將orderStatus由0->1 是需要冪等性的:

1update Order set orderStatus = 1 where OrderId = 'orderid' and orderStatus = 0

        當orderStatus 處於0,1兩種狀態時,對訂單執行0->1 的狀態流轉操作應該是具有冪等性的。這時候需要在執行update操作之前檢測orderStatus是否已經=1,如果已經=1則直接返回true即可。

        但是如果此時orderStatus = 2,再進行訂單狀態0->1 時操作就無法成功,但是冪等性是針對同一個請求的,也就是針對同一個requestid保持冪等,這時候再執行:

1update Order set orderStatus = 1 where OrderId = 'orderid' and orderStatus = 0

        接口會返回失敗,系統沒有產生修改,如果再發一次,requestid是相同的,對系統同樣沒有產生修改。

4 解決方案4.1 全局唯一ID

        如果使用全局唯一ID,就是根據業務的操作和內容生成一個全局ID,在執行操作前先根據這個全局唯一ID是否存在,來判斷這個操作是否已經執行。如果不存在則把全局ID,存儲到存儲系統中,比如資料庫、Redis等。如果存在則表示該方法已經執行。

        使用全局唯一ID是一個通用方案,可以支持插入、更新、刪除業務操作。但是這個方案看起來很美但是實現起來比較麻煩,下面的方案適用於特定的場景,但是實現起來比較簡單。

4.2 去重表

        這種方法適用於在業務中有唯一標的插入場景中,比如在以上的支付場景中,如果一個訂單只會支付一次,所以訂單ID可以作為唯一標識。這時,我們就可以建一張去重表,並且把唯一標識作為唯一索引,在我們實現時,把創建支付單據和寫入去去重表,放在一個事務中,如果重複創建,資料庫會拋出唯一約束異常,操作就會回滾。

4.3 插入或更新

        這種方法插入並且有唯一索引的情況,比如我們要關聯商品品類,其中商品的ID和品類的ID可以構成唯一索引,並且在數據表中也增加了唯一索引。這時就可以使用InsertOrUpdate操作。在mysql資料庫中如下:

1insert into goods_category (goods_id,category_id,create_time,update_time)
2    values(
3    on DUPLICATE KEY UPDATE update_time=now()

4.4 多版本控制

        這種方法適合在更新的場景中,比如我們要更新商品的名字,這時我們就可以在更新的接口中增加一個版本號,來做冪等:

1boolean updateGoodsName(int id,String newName,int version);

        在實現時可以如下:

1update goods set name=

4.5 狀態機控制

        這種方法適合在有狀態機流轉的情況下,比如就會訂單的創建和付款,訂單的付款肯定是在之前,這時我們可以通過在設計狀態欄位時,使用int類型,並且通過值類型的大小來做冪等,比如訂單的創建為0,付款成功為100,付款失敗為99。在做狀態機更新時,我們就這可以這樣控制:

1update goods_order set status=#{status} where id=#{id} and status<#{status}

        以上就是保證接口冪等性的一些方法。

5 總結

        冪等性設計不能脫離業務來討論,一般情況下,去重表同時也是業務數據表,而針對分布式的去重ID,可以參考以下幾種方式:

UUID

Snowflake

資料庫自增ID

業務本身的唯一約束

業務欄位+時間戳拼接

相關焦點

  • 深入的理解冪等性
    什麼是冪等性?為什麼要考慮冪等性?開始今天,是6月10日。前天,2019年度的高考結束了。又有一批莘莘學子,放下了心中的重擔,踏上新的旅程。今天,我們要聊聊冪等性。對了,如果你剛考完,準備以後幹程式設計師的話,不妨看看。(這個開始段落的模式,估計還能用10天)
  • 冪等性學習及接口的冪等性
    冪等性學習一:什麼是冪等性在這裡需要有以下幾個問題需要注意:1:冪等性的實質是一次或多次請求同一個資源,其結果是相同的。其關注的是對資源產生的影響(副作用)而不是結果,結果可以不同。之後在根據這個id執行此操作,無論執行多少次其結果和第一次執行後的結果一樣;4:冪等性關注的是以後的多次請求是否對資源產生了副作用,而不是關注的結果;5:需要說明的是網絡超時、服務宕機等問題,不是冪等的範圍。
  • 接口的冪等性,Restful接口的冪等性
    接口的冪等性,Restful接口的冪等性說到接口的冪等性,我們先看看什麼是冪等?什麼是冪等?在訂單的支付中,如果沒有冪等性,接口的重試可能造成重複支付。接口的冪等性有了上面的介紹,對接口的冪等性的理解就簡單了,接口的冪等性就是多次調用一個接口的效果是一樣的,比如更新一個訂單,更新一次和相同請求發送N次的結果一樣。
  • 保證MQ消費消息的冪等性,真可以用版本號的方式?
    你們都說可以用版本號來解決冪等性消費?什麼才是消息冪等性消費的根本性問題?隨著系統的複雜性不斷增加,多數系統都會引入MQ來進行解耦,其實從引入MQ的初衷來說,多數系統是為了解耦多個模塊帶來的複雜性,而有些「架構師」卻說的:為了解決性能問題。
  • SpringBoot 接口冪等性的實現方案
    在計算機中編程中,一個冪等操作的特點是其任意多次執行所產生的影響均與一次執行的影響相同。冪等函數或冪等方法是指可以使用相同參數重複執行,並能獲得相同結果的函數。這些函數不會影響系統狀態,也不用擔心重複執行會對系統造成改變。二、什麼是接口冪等性在HTTP/1.1中,對冪等性進行了定義。
  • 接口的冪等性的多重考慮,你會了嗎?
    本文約: 2300字 預計閱讀時間:6分鐘目錄前言正文1 接口冪等性2.5 樂觀鎖(更新操作)2.6 悲觀鎖(更新操作)結語前言今天的主題:接口冪等性的解決方案當然,在接口設計中我們要考慮很多問題,安全性,格式,設計等等,今天我們先來聊聊,在高並發環境下,接口冪等性的解決方案有哪些。正文1 接口冪等性就是說在多次相同的操作下保證最終的結果是一致的。
  • 「微服務架構」微服務架構中的數據一致性
    在微服務中,一個邏輯上原子操作可以經常跨越多個微服務。即使是單片系統也可能使用多個資料庫或消息傳遞解決方案。使用多個獨立的數據存儲解決方案,如果其中一個分布式流程參與者出現故障,我們就會面臨數據不一致的風險 - 例如在未下訂單的情況下向客戶收費或未通知客戶訂單成功。在本文中,我想分享一些我為使微服務之間的數據最終保持一致而學到的技術。
  • 以微信紅包為例講解實現接口冪等性的幾種方案
    類似的接口在我們的開發過程中會有很多,比如在電商的下單過程中:訂單創建接口,第一次調用返回超時了,重試機制一般會再次調用這個接口,此時我們不能因為這個接口被調了兩次就創建兩個一樣的訂單;庫存扣減接口,支付接口也是類似的邏輯;今天的文章就來講講什麼是接口的冪等性,並介紹幾種實現接口冪等性的方案
  • .NET 微服務實戰之必須得面對的分布式問題
    這三個問題也是很多面試官在面試的時候檢驗應聘者是否有實踐過分布式系統的經驗的標準之一,而微服務作為分布式系統的架構風格,在實施過程中也無法倖免以上問題。在分布式場景下冪等性的保證是無法避免的,網絡是存在不確定性的,一個請求可能會成功,但也會因為客觀因素導致失敗,那麼重新發起請求就無發避免的了,那麼如何保證我不會重複創建數據與數據被覆蓋呢?下文我將從數據關聯,分布式事務和冪等性三個角度進行敘述方案。
  • 一鍵搞定 ASP.NET Core Web API 冪等性
    前言API的冪等性
  • 微服務架構的分布式容錯 · SOSP 2019
    S 模型與微服務架構微服務架構的複雜性來源於服務之間的大量交互以及網絡請求的不確定性,調用路徑上的任何服務超時或者宕機都可能影響用戶請求的處理,服務的宕機也可能會造成用戶無法感知請求的結果、系統處於異常狀態並無法回滾等問題。
  • 高並發分布式場景最全的MQ消息重發冪等性解決方案
    一、冪等性是什麼?在編程中,一個冪等操作的特點是其任意多次執行所產生的影響均與一次執行的影響相同。冪等函數,或冪等方法,是指可以使用相同參數重複執行,並能獲得相同結果的函數。三、防重複消息冪等性解決方案1、生產者重複發消息MQ冪等性設計MQ在接收生產者傳來的消息時,MQ內部會生成一個全局唯一與業務無關的消息ID:inner-msg-id。
  • 微服務是什麼?十分鐘了解微服務架構
    微服務架構圖1: 單體式和微服務這些挫折引出了微服務架構風格:將應用程式構建為服務套件。我們並不是說微服務風格是新穎的或創新的,它的根源至少可以追溯到Unix的設計原則。但我們確實認為,沒有足夠多的人考慮使用微服務架構,如果他們使用了,那麼許多軟體開發將會更好。
  • soa和微服務的區別
    人們對於微服務的概念進行了大量的討論,其中有許多討論是關於微服務與SOA之間的關聯。詳細請看下文分析 在目前來看,微服務還是一種相對較新的技術,架構師與開發者們通常所使用的一些輔助性工具也還處於發展階段,以上所提及的這些問題可能遲早會得到解決。
  • RabbitMQ消息中間件技術精講10 高級篇三 冪等性保障不重複消費
    利用冪等性保障消息不被重複消費本文主要內容:一:冪等性概念什麼是冪等性?關於冪等性更詳細的介紹,可以參見《拓展知識一:冪等性》這篇文章。冪等性我們可以借鑑資料庫的樂觀鎖機制來理解:比如,我們執行一條更新庫存的sql語句:update table set count = count -1 where id = 1流程說明:在高並發的情況下
  • 如何確保分布式場景下的並發冪等性?
    冪等是數學中的一個概念,它表示如果一個東西進行1次變換或進行N次變換之後,所產生的效果如果相同的話,就稱為冪等。在程序上,如果我們使用相同的請求調用這個接口一次和調用這個接口多次,對系統所產生的影響是相同的。如果一個接口滿足這個特性,那麼我們就說這個接口是冪等的。
  • 一文講透微服務架構下如何保證事務的一致性
    而微服務架構是將單個服務拆分成一系列小服務,且這些小服務都擁有獨立的進程,彼此獨立,很好地解決了傳統單體應用的上述問題,但是在微服務架構下如何保證事務的一致性呢?本文作者將為大家詳細解答。從本地事務到分布式事務的演變什麼是事務?回答這個問題之前,我們先來看一個經典的場景:支付寶等交易平臺的轉帳。
  • 微服務架構春天 微軟Service Fabric開源
    微軟在去年也宣布即將公布自己的微服務架構,當時開源了 Service Fabric的.NET SDK 部分,而轉眼一年時間已經過去了,微軟終於在上周開源了自己的Service Fabric。Github截圖  微服務架構優勢一般為複雜可控、靈活可擴展、獨立部署、開發展對性強等等,當然最重要的還有降低TCO。
  • 冪函數是奇函數嗎?是增函數嗎?所有冪函數具有的性質都在這!
    冪函數的奇偶性⑴當冪函數的冪指數a是奇數時,冪指數y=x^a是奇函數。⑷當冪函數的冪指數a是分數且分母是偶數時,冪函數y=x^a是非奇非偶函數。當冪指數為分數且分母是偶數時說明該冪指數要開偶次方根,所以x取值範圍是(0,+∞),所以此時的冪函數的定義域並不關於原點對稱,即冪函數y=x^a此時是非奇非偶函數。
  • 厲害了,教你用 Spring Cloud 實現微服務
    分布式的管理Martin自己也說了,每個人對微服務都可以有自己的理解,不過大概的標準還是有一些的。還有一般微服務在系統內部,通常是無 狀態的,用戶登錄信息和權限管理最好有一個統一的地方維護管理(OAuth)。