理解HTTP等冪性

來源:互聯網
上載者:User

基於HTTP協議的Web API是時下最為流行的一種分布式服務提供者式。無論是在大型互連網應用還是企業級架構中,我們都見到了越來越多的SOA或RESTful的Web API。為什麼Web API如此流行呢?我認為很大程度上應歸功於簡單有效HTTP協議。HTTP協議是一種分布式的面向資源的網路應用程式層協議,無論是伺服器端提供Web服務,還是用戶端消費Web服務都非常簡單。再加上瀏覽器、Javascript、AJAX、JSON以及HTML5等技術和工具的發展,互連網應用架構設計表現出了從傳統的PHP、JSP、ASP.NET等伺服器端動態網頁向Web API + RIA(富互連網應用)過渡的趨勢。Web API專註於提供商務服務,RIA專註於使用者介面和互動設計,從此兩個領域的分工更加明晰。在這種趨勢下,Web API設計將成為伺服器端程式員的必修課。然而,正如簡單的Java語言並不意味著高品質的Java程式,簡單的HTTP協議也不意味著高品質的Web API。要想設計出高品質的Web API,還需要深入理解分布式系統及HTTP協議的特性。

 

等冪性定義

本文所要探討的正是HTTP協議涉及到的一種重要性質:等冪性(Idempotence)。在HTTP/1.1規範中等冪性的定義是:

Methods can also have the property of "idempotence" in that (aside from error or expiration issues) the side-effects of N > 0 identical requests is the same as for a single request.

從定義上看,HTTP方法的等冪性是指一次和多次請求某一個資源應該具有同樣的副作用。等冪性屬於語義範疇,正如編譯器只能協助檢查語法錯誤一樣,HTTP規範也沒有辦法通過訊息格式等文法手段來定義它,這可能是它不太受到重視的原因之一。但實際上,等冪性是分布式系統設計中十分重要的概念,而HTTP的分布式本質也決定了它在HTTP中具有重要地位。

 

分散式交易 vs 等冪設計

為什麼需要等冪性呢?我們先從一個例子說起,假設有一個從賬戶取錢的遠程API(可以是HTTP的,也可以不是),我們暫時用類函數的方式記為: 

bool withdraw(account_id, amount)

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

圖1

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

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

int create_ticket()

bool idempotent_withdraw(ticket_id, account_id, amount)

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是等冪的,所以任何一步由於網路等原因失敗或逾時,用戶端都可以重試,直到獲得結果。2所示:

圖2

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

 

HTTP的等冪性

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

HTTP GET方法用於擷取資源,不應有副作用,所以是等冪的。比如:GET http://www.bank.com/account/123456,不會改變資源的狀態,不論調用一次還是N次都沒有副作用。請注意,這裡強調的是一次和N次具有相同的副作用,而不是每次GET的結果相同。GET http://www.news.com/latest-news這個HTTP請求可能會每次得到不同的結果,但它本身並沒有產生任何副作用,因而是滿足等冪性的。

HTTP DELETE方法用於刪除資源,有副作用,但它應該滿足等冪性。比如:DELETE http://www.forum.com/article/4231,調用一次和N次對系統產生的副作用是相同的,即刪掉id為4231的文章;因此,調用者可以多次調用或重新整理頁面而不必擔心引起錯誤。

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

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. ...... If a resource has been created on the origin server, the response SHOULD be 201 (Created) and contain an entity which describes the status of the request and refers to the new resource, and a Location header.

The PUT method requests that the enclosed entity be stored under the supplied Request-URI. If the Request-URI refers to an already existing resource, the enclosed entity SHOULD be considered as a modified version of the one residing on the origin server. If the Request-URI does not point to an existing resource, and that URI is capable of being defined as a new resource by the requesting user agent, the origin server can create the resource with that URI.

POST所對應的URI並非建立的資源本身,而是資源的接收者。比如:POST http://www.forum.com/articles的語義是在http://www.forum.com/articles下建立一篇文章,HTTP響應中應包含文章的建立狀態以及文章的URI。兩次相同的POST請求會在伺服器端建立兩份資源,它們具有不同的URI;所以,POST方法不具備等冪性。而PUT所對應的URI是要建立或更新的資源本身。比如:PUT http://www.forum/articles/4231的語義是建立或更新ID為4231的文章。對同一URI進行多次PUT的副作用和一次PUT是相同的;因此,PUT方法具有等冪性。

在介紹了幾種操作的語義和等冪性之後,我們來看看如何通過Web API的形式實現前面所提到的取款功能。很簡單,用POST /tickets來實現create_ticket;用PUT /accounts/account_id/ticket_id&amount=xxx來實現idempotent_withdraw。值得注意的是嚴格來講amount參數不應該作為URI的一部分,真正的URI應該是/accounts/account_id/ticket_id,而amount應該放在請求的body中。這種模式可以應用於很多場合,比如:論壇網站中防止意外的重複發帖。

 

總結

上面簡單介紹了等冪性的概念,用等冪設計取代分散式交易的方法,以及HTTP主要方法的語義和等冪性特徵。其實,如果要追根溯源,等冪性是數學中的一個概念,表達的是N次變換與1次變換的結果相同,有興趣的讀者可以從Wikipedia上進一步瞭解。

 

參考

RFC 2616, Hypertext Transfer Protocol -- HTTP/1.1, Method Definitions

The Importance of Idempotence

stackoverflow -  PUT vs POST in REST

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.