深入理解分布式系統中的HTTP冪等性

2021-08-20 09:29:03 字數 2424 閱讀 6847

說起冪等性,可能很多人並不怎麼熟悉,在開發中也沒有去關注它。這是對於乙個初級程式猿來說的,但是,如果作為乙個入行多年的中高階開發,你還不了解冪等性,那麼,你應該感到臉紅

冪等性(idempotence)是http協議涉及到的一種重要性質。

冪等性是指一次和多次請求某乙個資源應該具有同樣的***。」這是其定義,那麼,我們應該怎麼理解呢?

舉個例子吧:

我們遠端修改乙個使用者的銀行賬戶資訊(比方說給使用者充值1000元),當我們多次執行這個操作的時候,並不是給這個使用者充值了多次,而是只充值了一次。也就是說給使用者充值這個操作,不管我們是執行一次還是100次,其最終產生的效果是一樣的。但這並不是說具有同樣的返回,這個在下文會說到。

以下以乙個例子做說明:

假設有乙個從賬戶取錢的遠端api(可以是http的,也可以不是),我們暫時用類函式的方式記為:

withdraw

withdraw的語義是從account_id對應的賬戶中扣除amount數額的錢;如果扣除成功則返回true,賬戶餘額減少amount;如果扣除失敗則返回false,賬戶餘額不變。值得注意的是:和本地環境相比,我們不能輕易假設分布式環境的可靠性。一種典型的情況是withdraw請求已經被伺服器端正確處理,但伺服器端的返回結果由於網路等原因被掉丟了,導致客戶端無法得知處理結果。如果是在網頁上,一些不恰當的設計可能會使使用者認為上一次操作失敗了,然後重新整理頁面,這就導致了withdraw被呼叫兩次,賬戶也被多扣了一次錢。

這個問題的解決方案一是採用分布式事務,通過引入支援分布式事務的中介軟體來保證withdraw功能的事務性。分布式事務的優點是對於呼叫者很簡單,複雜性都交給了中介軟體來管理。缺點則是一方面架構太重量級,容易被綁在特定的中介軟體上,不利於異構系統的整合;另一方面分布式事務雖然能保證事務的acid性質,而但卻無法提供效能和可用性的保證。

另一種更輕量級的解決方案是冪等設計。我們可以通過一些技巧把withdraw變成冪等的,比如:

create_ticket的語義是獲取乙個伺服器端生成的唯一的處理號ticket_id,它將用於標識後續的操作。idempotent_withdraw和withdraw的區別在於關聯了乙個ticket_id,乙個ticket_id表示的操作至多只會被處理一次,每次呼叫都將返回第一次呼叫時的處理結果。這樣,idempotent_withdraw就符合冪等性了,客戶端就可以放心地多次呼叫。

基於冪等性的解決方案中乙個完整的取錢流程被分解成了兩個步驟:1.呼叫create_ticket()獲取ticket_id;2.呼叫idempotent_withdraw(ticket_id, account_id, amount)。雖然create_ticket不是冪等的,但在這種設計下,它對系統狀態的影響可以忽略,加上idempotent_withdraw是冪等的,所以任何一步由於網路等原因失敗或超時,客戶端都可以重試,直到獲得結果。

和分布式事務相比,冪等設計的優勢在於它的輕量級,容易適應異構環境,以及效能和可用性方面。在某些效能要求比較高的應用,冪等設計往往是唯一的選擇。

http協議本身是一種面向資源的應用層協議,但對http協議的使用實際上存在著兩種不同的方式:一種是restful的,它把http當成應用層協議,比較忠實地遵守了http協議的各種規定;另一種是soa的,它並沒有完全把http當成應用層協議,而是把http協議作為了傳輸層協議,然後在http之上建立了自己的應用層協議。本文所討論的http冪等性主要針對restful風格的,不過正如上一節所看到的那樣,冪等性並不屬於特定的協議,它是分布式系統的一種特性;所以,不論是soa還是restful的web api設計都應該考慮冪等性。下面將介紹http get、delete、put、post四種主要方法的語義和冪等性。

http get方法用於獲取資源,不應有***,所以是冪等的。比如:get 不會改變資源的狀態,不論呼叫一次還是n次都沒有***。請注意,這裡強調的是一次和n次具有相同的***,而不是每次get的結果相同。get 這個http請求可能會每次得到不同的結果,但它本身並沒有產生任何***,因而是滿足冪等性的。

http delete方法用於刪除資源,有***,但它應該滿足冪等性。比如:delete 呼叫一次和n次對系統產生的***是相同的,即刪掉id為4231的帖子;因此,呼叫者可以多次呼叫或重新整理頁面而不必擔心引起錯誤。

比較容易混淆的是http post和put。post和put的區別容易被簡單地誤認為「post表示建立資源,put表示更新資源」;而實際上,二者均可用於建立資源,更為本質的差別是在冪等性方面。在http規範中對post和put是這樣定義的:

post所對應的uri並非建立的資源本身,而是資源的接收者。比如:post 的語義是在下建立一篇帖子,http響應中應包含帖子的建立狀態以及帖子的uri。兩次相同的post請求會在伺服器端建立兩份資源,它們具有不同的uri;所以,post方法不具備冪等性。而put所對應的uri是要建立或更新的資源本身。比如:put 的語義是建立或更新id為4231的帖子。對同一uri進行多次put的***和一次put是相同的;因此,put方法具有冪等性。

深入理解分布式事務

碼農網 吳極心 當資料庫單錶一年產生的資料超過1000w,那麼就要考慮分庫分表,具體分庫分表的原理在此不做解釋,以後有空詳細說,簡單的說就是原來的乙個資料庫變成了多個資料庫。這時候,如果乙個操作既訪問01庫,又訪問02庫,而且要保證資料的一致性,那麼就要用到分布式事務。所謂的soa化,就是業務的服務...

深入理解分布式事務

當資料庫單錶一年產生的資料超過1000w,那麼就要考慮分庫分表,具體分庫分表的原理在此不做解釋,以後有空詳細說,簡單的說就是原來的乙個資料庫變成了多個資料庫。這時候,如果乙個操作既訪問01庫,又訪問02庫,而且要保證資料的一致性,那麼就要用到分布式事務。所謂的soa化,就是業務的服務化。比如原來單機...

深入理解分布式事務

我在上一期介紹了spring的事務原理 詳情見 深入理解spring事務原理 spring事務 本質是單機下的事務,是由資料庫本身保證的。今天,我將介紹一種比較複雜的事務 分布式事務 當資料庫單錶一年產生的資料超過1000w,那麼就要考慮分庫分表,具體分庫分表的原理在此不做解釋,以後有空詳細說,簡單...