转:聊聊开发中幂等性问题(*)
【README】 這是一篇非常棒的, 講解冪等性問題的post, 感謝原文作者;
轉自:?https://juejin.cn/post/6844903815552958477?
?
冪等 (idempotence) 的概念
冪等的數(shù)學概念
冪等是源于一種數(shù)學概念。其主要有兩個定義
如果在一元運算中,x 為某集合中的任意數(shù),如果滿足 f(x) = f(f(x)) ,那么該 f 運算具有冪等性,比如絕對值運算 abs(a) = abs(abs(a)) 就是冪等性函數(shù)。
如果在二元運算中,x 為某集合中的任意數(shù),如果滿足 f(x,x) = x,前提是 f 運算的兩個參數(shù)均為 x,那么我們稱 f 運算也有冪等性,比如求大值函數(shù) max(x,x) = x 就是冪等性函數(shù)。
冪等性在開發(fā)中的概念
在數(shù)學中冪等的概念或許比較抽象,但是在開發(fā)中冪等性是極為重要的。簡單來說,對于同一個系統(tǒng),在同樣條件下,一次請求和重復多次請求對資源的影響是一致的,就稱該操作為冪等的。比如說如果有一個接口是冪等的,當傳入相同條件時,其效果必須是相同的。
特別是對于現(xiàn)在分布式系統(tǒng)下的 RPC 或者 Restful 接口互相調用的情況下,很容易出現(xiàn)由于網(wǎng)絡錯誤等等各種原因導致調用的時候出現(xiàn)異常而需要重試,這時候就必須保證接口的冪等性,否則重試的結果將與第一次調用的結果不同,如果有個接口的調用鏈 A->B->C->D->E,在 D->E 這一步發(fā)生異常重試后返回了錯誤的結果,A,B,C也會受到影響,這將會是災難性的。
在生活中常見的一些要求冪等性的例子:
冪等性與并發(fā)安全
在查閱網(wǎng)絡資料的時候,我看到許多文章把冪等性和并發(fā)安全的問題有些混淆了。冪等性是系統(tǒng)接口對外的一種承諾,而不是實現(xiàn),承諾多次相同的操作的結果都會是一樣的。而并發(fā)安全問題是當多個線程同時對同一個資源操作時,由于操作順序等原因導致結果不正確。
這兩個實際上是完全獨立的兩個問題,比如說同一筆訂單即使你不停的提交支付,如果扣除了多次錢,就說明該操作不冪等。而有多筆訂單同時進行支付,最后扣除金額不是這多筆金額的總和,那么說明該操作有并發(fā)安全問題。所以冪等性和并發(fā)安全是完全兩個維度的問題,要分開討論解決。
我在一些討論冪等性的文章中看到中給出的解決方案為‘悲觀鎖’和‘樂觀鎖’,這兩個方案可以很好的解決并發(fā)問題,但是卻不應該是冪等性問題的解決方案,特別是悲觀鎖是用于防止多個線程同時修改一個資源的。倒是樂觀鎖的版本號機制可以勉強以 token 或者狀態(tài)標識 作為版本號來實現(xiàn)冪等性(下文解釋token 和狀態(tài)標識),勉強說的過去。
所以說冪等性與并發(fā)安全是不同的,在本文就只討論冪等性的問題,對于并發(fā)安全問題不做討論
Http 協(xié)議與冪等性
如果把操作按照功能分類,那就是增刪改查四種,在 http 協(xié)議中則表現(xiàn)為 Get、Post、Put、Delete 四種。
查詢操作 (Get)
Get 方法用于獲取資源,不應當對系統(tǒng)資源進行改變,所以是冪等的。注意這里的冪等提現(xiàn)在對系統(tǒng)資源的改變,而不是返回數(shù)據(jù)的結果,即使返回結果不相同但是該操作本身沒有副作用,所以冪等。
刪除操作 (Delete)
Delete 方法用于刪除資源,雖然改變了系統(tǒng)資源,但是第一次和第N次刪除操作對系統(tǒng)的作用是相同的,所以是冪等的。比如要刪除一個 id 為 1234 的資源,可能第一次調用時會刪除,而后面所有調用的時候由于系統(tǒng)中已經(jīng)沒有這個 id 的資源了,但是第一次操作和后面的操作對系統(tǒng)的作用是相同的,所以這也是冪等的,調用者可以多次調用這個接口不必擔心錯誤。
修改操作 (Put)
修改操作有可能是冪等的也可能不冪等。如果修改的資源為固定的,比如說把賬戶中金額改為 1000 元,無論調用幾次都是冪等的。假如資源不固定,比如賬戶中金額減少50元,調用一次和調用多次的結果肯定不一樣,這時候就不冪等了。在修改操作中想要冪等在下文中討論。
2019-08-13 修改
原文對Put協(xié)議定義有錯誤,Put操作必須為冪等的,即如果聲明為Put協(xié)議時就相當于對外聲明這個接口是冪等的。所以對于原文舉例說賬戶中金額減少50元這種操作在Put協(xié)議中是不允許的,只能做類似于賬戶中金額改為 1000 元的操作
參考:restfulapi.net/idempotent-…
新增操作 (Post)
Post 新增操作天生就不是一個冪等操作,其在 http 協(xié)議的定義如下:
The POST method is used to request that the origin server accept the entity enclosed in the request as a new subordinate of the resource identified by the Request-URI in the Request-Line.
(www.w3.org/Protocols/r…)
在其定義中表明了 Post 請求用于創(chuàng)建新的資源,這意味著每次調用都會在系統(tǒng)中產(chǎn)生新的資源,所以該操作注定不是冪等操作。這時候想要冪等就必須在業(yè)務中實現(xiàn),方案在下文會討論。
實現(xiàn)冪等性的方案
在上面提到的冪等性還是比較理論,下面結合一些常見的實際業(yè)務場景來討論冪等性設計方案。
去重表
利用數(shù)據(jù)庫的特性來實現(xiàn)冪等。通常是在表上構建一個唯一索引,那么只要某一個數(shù)據(jù)構建完畢,后面再次操作也無法成功寫入。
常見的業(yè)務就是博客系統(tǒng)點贊功能,一個用戶對一個博文點贊后,就把用戶 id 與 博文 id 綁定,后續(xù)該用戶點贊同一個博文就無法插入了。或是在金融系統(tǒng)中,給用戶創(chuàng)建金融賬戶,一個用戶肯定不能有多個賬戶,就在賬戶表中增加唯一索引來存儲用戶 id,這樣即使重復操作用戶也只能擁有一個賬戶。
狀態(tài)標識
狀態(tài)標識是很常見的冪等設計方式,主要思路就是通過狀態(tài)標識的變更,保證業(yè)務中每個流程只會在對應的狀態(tài)下執(zhí)行,如果標識已經(jīng)進入下一個狀態(tài),這時候來了上一個狀態(tài)的操作就不允許變更狀態(tài),保證了業(yè)務的冪等性。
狀態(tài)標識經(jīng)常用在業(yè)務流程較長,修改數(shù)據(jù)較多的場景里。最經(jīng)典的例子就是訂單系統(tǒng),假如一個訂單要經(jīng)歷 創(chuàng)建訂單 -> 訂單支付\取消 -> 賬戶計算 -> 通知商戶 這四個步驟。那么就有可能一筆訂單支付完成后去賬戶里扣除對應的余額,消耗對應的優(yōu)惠卷。但是由于網(wǎng)絡等原因返回了錯誤信息,這時候就會重試再次去進行賬戶計算步驟造成數(shù)據(jù)錯誤。
所以為了保證整個訂單流程的冪等性,可以在訂單信息中增加一個狀態(tài)標識,一旦完成了一個步驟就修改對應的狀態(tài)標識。比如訂單支付成功后,就把訂單標識為修改為支付成功,現(xiàn)在再次調用訂單支付或者取消接口,會先判斷訂單狀態(tài)標識,如果是已經(jīng)支付過或者取消訂單,就不會再次支付了。
Token 機制
Token 機制應該是適用范圍最廣泛的一種冪等設計方案了,具體實現(xiàn)方式也很多樣化。但是核心思想就是每次操作都生成一個唯一 Token 憑證,服務器通過這個唯一憑證保證同樣的操作不會被執(zhí)行兩次。這個 Token 除了字面形式上的唯一字符串,也可以是多個標志的組合(比如上面提到的狀態(tài)標志),甚至可以是時間段標識等等。
舉個例子,在論壇中發(fā)布一個新帖子,這是一個典型的 Post 新增操作,要怎樣防止用戶多次點擊提交導致產(chǎn)生多個同樣的帖子呢。可以讓用戶提交的時候帶一個唯一 Token,服務器只要判斷該 Token 存在了就不允許提交,便能保證冪等性。
上面這個例子比較容易理解,但是業(yè)務比較簡單。由于 Token 機制適用較廣,所以其設計中要注意的要求也會根據(jù)業(yè)務不同而不同。
Token 在何時生成,怎么生成?這是該機制的核心,就拿上面論壇系統(tǒng)來說,如果你在用戶提交帖子的時候才生成 Token,那用戶每次點提交都會生成新的 Token 然后都能提交成功,就不是冪等的了。必須在用戶提交內(nèi)容之前,比如進入編輯頁面的時候生成 Token,用戶在提交的時候內(nèi)容帶著 Token 一起提交,對于同一個頁面無論用戶提交多少次,就至多能成功一次。所以 Token 生成的時機必須保證能夠使該操作具多次執(zhí)行都是相同的效果才行。使用 Token 機制就要求開發(fā)者對業(yè)務流程有較好的理解。
結語
冪等性是開發(fā)當中很常見也很重要的一個需求。尤其是金融、支付等行業(yè)對其要求更加嚴格,既要有好的性能也要有嚴格的冪等性。除了對其概念的掌握,理解自身業(yè)務需求更是實現(xiàn)冪等功能的要點,必須處理好每一個結點細節(jié),一旦某個地方?jīng)]有設計完善,最后的結果可能仍舊達不到要求。
作者:zzzzbw
鏈接:https://juejin.cn/post/6844903815552958477
來源:掘金
著作權歸作者所有。商業(yè)轉載請聯(lián)系作者獲得授權,非商業(yè)轉載請注明出處。
?
總結
以上是生活随笔為你收集整理的转:聊聊开发中幂等性问题(*)的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: kafka命令行操作
- 下一篇: 转-Apache kafka 工作原理介