楠木軒

冪等性學習及接口的冪等性

由 漆雕佁 發佈於 經典

冪等性學習


一:什麼是冪等性


在這裏需要有以下幾個問題需要注意:

1:冪等性的實質是一次或多次請求同一個資源,其結果是相同的。其關注的是對資源產生的影響(副作用)而不是結果,結果可以不同。比如列表查詢的時候,一邊在save或者是update,而你這邊還是在select,其結果肯定是不同的,但是你的select操作並未對數據(資源)產生影響(副作用);

2:冪等性不僅僅只是一次或者多次請求的時候對資源沒有副作用。比如根據id對數據庫的查詢操作,此操作對數據庫沒有增刪改,所以多次查詢操作對數據庫結果是沒有任何影響的;

3:冪等性還包括了第一次請求資源的時候,對資源產生了副作用,但是在以後多次同樣的請求操作的時候,都不會在對資源產生副作用了。比如我們根據id更新訂單狀態從支付中變為支付完成這個操作,在執行第一次的時候,會更新為支付完成。之後在根據這個id執行此操作,無論執行多少次其結果和第一次執行後的結果一樣;

4:冪等性關注的是以後的多次請求是否對資源產生了副作用,而不是關注的結果;

5:需要説明的是網絡超時、服務宕機等問題,不是冪等的範圍。

冪等性是系統服務對外的一種承諾(注意,是一種承諾,而不是一種實現),接口服務提供方承諾只要調用接口成功了,外部多次調用對系統的影響是一致的。這裏需要強調一點就是,聲明為冪等的服務會認為調用方調用失敗是常態,是正常業務,並且允許在調用失敗後必然會有重試的。

來源:凱哥Java(kaigejava)


二:什麼情況下需要使用冪等

在我們開發中,經常會遇到一個頭疼的事情—重複提交的情況。重複提交情況有多種原因產生的。如由於網絡問題無法收到請求結果情況下而重新發起的請求或者是因為調用方前端操作抖動而造成的重複提交。

重複提交操作帶來的嚴重後果在交易系統、支付系統中因重複提交而產生的問題尤其的明顯。如:我們發起支付的時候向支付寶支付請求,無論是交易系統自身bug還是交易系統與支付寶之間的網絡問題導致重複發送,支付寶應該並且必須只能扣用户一次錢的。在這種需求下的系統在設計的時候,我們就需要將系統或者服務設計成冪等的。

三:冪等和防重複提交比較

重複提交:重複提交是在第一次請求成功的情況下,人為的進行多次操作,從而導致不滿足冪等性要求的服務多次改變數據狀態。

冪等:更多使用的情況是第一次請求知道結果(比如常見的網絡抖動導致連接超時)或者失敗異常情況下,發起多次請求的,其目的是多次確認第一次請求成功,卻不會因為多次請求而出現多次的狀態變化。

什麼情況下需要保障冪等性?

在這裏,我們以sql為例來講解。在下面三種場景中,只要第三種場景需要開發人員使用其他策略來保障冪等性:

1:查詢情況

Select * from table where id = 2

無論執行多少次都不會對資源造成副作用,所以可以説是天然的冪等

2:根據id更新

Update table set status =1 where id =2

無論執行成功多少次狀態都是一致的,第一次執行成功對資源造成的副作用和多次執行成功對數據造成的副作用是一樣的。因此可以説這種場景也是冪等操作

3:不是冪等情況

Update table set version = version+1 where id = 2

每次執行的結果都會發生變化,也就是説對資源造成了副作用,這種不是冪等的。這種情況如果想要保證冪等,語句可以這麼寫:update table set version = version+1 where id = 2 and version = 1.這樣就可以保證冪等了。

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

冪等性的服務可以使得客户端的處理業務邏輯變的簡單了,但是確實以犧牲服務端邏輯變複雜為代價的。因為在滿足冪等服務的需求下邏輯至少需要包含以下兩點:

1:首選去查詢上一次的執行狀態(結果),如果沒有則認為是第一次請求。這樣就增加了業務難度

2:在服務改變狀態的業務邏輯前,保證防重複提交的邏輯

冪等的不足:

通過上面我們知道了冪等服務是以犧牲服務提供者邏輯和成本為代價的。所以,是否有必要,需要根據具體場景具體來分析的。因此除了業務上的特殊要求外,儘量不要提供冪等的接口。

1:增加了額外的控制冪等的業務邏輯,複雜了業務功能;

2:把並行執行的功能改為串行執行,這樣就降低了執行的效率。

保證冪等策略

其實在保證冪等的業務會通過唯一的業務單號來保證的。也就是説相同的單號,被認為是同一筆業務。使用唯一的業務單號來確保,後面多次相同的業務單號的的處理邏輯和執行兄啊過是一致的。我們以常見的支付為例(在不考慮併發情況下),實現冪等很簡單:

1:先查詢一下訂單是否已經支付過

2:如果已經支付過,則返回支付成功;如果沒有支付,在進行支付流程操作後,將訂單狀態修改為已支付。