如何實現乙個冪等介面

2021-10-04 22:30:41 字數 2326 閱讀 1819

首先我們要搞清楚,何為冪等。冪等本來是乙個數學中的概念,即使f(x)=f(f(x))。引入到計算機領域後,指對同乙個介面或方法,使用同樣的條件,一次請求和任意多次請求對系統的影響是一致的。

那通常我們有哪些手段來實現乙個冪等介面或冪等函式呢?

我們還是先來將介面分下類,將介面分為查詢,刪除和交易型別:

查詢型別:

查詢型別的介面,天然具有冪等性,因為查詢交易一般不會對系統資源進行變更。比如根據使用者的userid,查詢使用者的賬戶餘額。如果賬戶的餘額沒有變化,那麼查詢一次得到的結果和查詢多次得到的結果肯定是一樣的。

刪除型別:

刪除型別的介面,也一般都是冪等的,比如根據流水號,刪除使用者的操作記錄。同樣的流水號,刪除一次,刪除多次,返回的結果可能不一樣,但影響是一樣的,都只是把這條資料刪除了。

交易型別:

我們討論的主角應該是交易型別。比如說轉賬,如果使用者只操作了一次轉賬,但因為客戶端呼叫服務端超時重發,或者使用了mq的系統,mq訊息重發,又或者單純的系統bug,導致服務端轉賬介面被呼叫了2次或更多次,那如何控制呼叫多次和呼叫1次,對使用者的影響是一樣的呢?即使用者最終只是轉了一筆錢。這也就是我們今天的話題,如何保證交易的冪等性。

介面冪等通常的設計方法通常有:

a):運用資料庫的唯一索引進行控制:

對於上面轉賬的例子。在設計服務端系統的轉賬介面時,可以在介面上設計乙個流水號,或者乙個轉賬id。客戶端在呼叫服務端時,需要將轉賬id傳送到服務端。服務端接收到訊息後,先儲存一條轉賬交易記錄transfer_log。transfer_log中將轉賬id設計成唯一索引,這樣如果客戶端一次轉賬交易呼叫了服務端多次,但多次用的是同乙個轉賬id,那麼服務端只有在第一次接收到該轉賬id時,可以成功入庫,後續再接收到相同轉賬id,因為資料庫唯一索引的控制,會入庫失敗,入庫失敗後,直接中斷後續操作直接返回客戶端乙個錯誤碼,告訴該次轉賬重複。這樣就可以保證同樣乙個轉賬id,無論客戶端呼叫幾次,對服務端產生的影響都是跟呼叫1次是一樣的。

b): 運用資料庫cas機制(樂觀鎖)。

還是轉賬的例子,使用者a賬戶有餘額500元,需要轉走100元。

b1: 客戶端發起轉賬時,同時把使用者當前餘額上送給服務端。服務端進行賬戶餘額扣減時,判斷當前的餘額和客戶上送的餘額是否一致。

update transfer set amout=amout-100 where userid = 『使用者a』 and amount = 500。

上面這條語句意思就是,使用者賬戶餘額是500,轉出100,如果餘額不是500,就不轉。這樣如果使用者a的轉賬命令,客戶端只會成功執行1次,執行第2次時,因為餘額肯定已經不是500了,不會執行成功,這樣也是實現了該交易的冪等性。

b2: b1方案確實可以實現轉賬交易的冪等性,但有乙個漏洞。如果使用者的操作步驟是下面這樣的,就會出現問題:

第一步:使用者a有餘額500元,在手機上進行轉賬100元,因為某種原因,比如mq訊息重發。服務端分別在第1分鐘,和第5分鐘收到了重複的兩條訊息。第一條訊息服務端處理完成,成功從使用者a賬戶轉走100元,使用者餘額現在變成了400元。

第二步:使用者a當前餘額為400元,使用者b在第3分鐘給使用者a轉入了100元,第3分鐘時,使用者a的餘額又變成了500元。

第三步:第5分鐘,第一步的重複訊息到達服務端,服務端又進行成功的從使用者a賬戶上轉走了100元,因為這個時候使用者餘額已經恢復到了500元,update語句的條件成立,但其實是做了重複轉賬,這是不能接受的。

那這種情況我們可以怎樣進行處理呢?

我們可以在transfer表裡增加乙個version欄位。轉賬交易,客戶端必須將version傳到伺服器,每次發生轉入轉出,餘額有變化時,就將version加1,語句如下:

update transfer set amount-100, version=version+1 where userid = 『賬戶a』 and version=1

像這樣,通過version來控制金額的變化,就可以解決通過餘額來控制金額變化帶來的問題。

c): 使用狀態機進行控制:

還是上面轉賬的例子。假如客戶端轉賬中很多步驟,每一步都會更新乙個狀態,我們就可以通過狀態機來實現冪等。

比如轉賬交易中,服務端有三步操作,每步操作完成都記錄乙個狀態。

1-校驗使用者資訊通過。

2-校驗賬戶狀態通過。

3-校驗餘額通過

4.發起轉賬。

轉賬必須必須是在校驗餘額通過後,我們就可以通過狀態機來控制及資料庫的cas機制來實現冪等。

update transfer set status = 4 where transfer_id = 『001』 and status = 3;

因為同一筆轉賬id,status狀態由3轉為4,只能是1次成功,第二呼叫,由於條件中的status=3必定不成立,更新也不會成功。也可以控制同乙個轉賬id,必然只會進行一次轉賬。

Mysql實現冪等 基於資料庫實現冪等介面

tl dr 通過唯一編號確定同一請求,沒有唯一編號的自行生成。資料庫記錄操作狀態,資料庫事務保證資料一致性。概述通過http api進行通訊的系統,在支付或者只允許操作一次的相關場景中,對介面的冪等性有嚴格要求。介面的冪等性體現在 請求執行成功所得到的結果與次數無關 如果介面沒有實現冪等性,對於轉賬...

mysql事務保證冪等 如何保證介面的冪等性

什麼是冪等性 冪等性是系統服務對外一種承諾,承諾只要呼叫介面成功,外部多次呼叫對系統的影響是一致的。宣告為冪等的服務會認為外部呼叫失敗是常態,並且失敗之後必然會有重試。什麼情況下需要冪等 以sql為例 select col1 from tab1 wher col2 2,無論執行多少次都不會改變狀態,...

如何保證介面的冪等性

冪等性是系統服務對外一種承諾,承諾只要呼叫介面成功,外部多次呼叫對系統的影響是一致的。宣告為冪等的服務會認為外部呼叫失敗是常態,並且失敗之後必然會有重試。以sql為例 select col1 from tab1 wher col2 2,無論執行多少次都不會改變狀態,是天然的冪等。update tab...