理解HTTP冪等性

轉(zhuǎn)載自 Programming.log - a place to keep my thoughts on programming

基于HTTP協(xié)議的Web API是時(shí)下最為流行的一種分布式服務(wù)提供方式。無(wú)論是在大型互聯(lián)網(wǎng)應(yīng)用還是企業(yè)級(jí)架構(gòu)中,我們都見(jiàn)到了越來(lái)越多的SOA或RESTful的Web API。為什么Web API如此流行呢?我認(rèn)為很大程度上應(yīng)歸功于簡(jiǎn)單有效的HTTP協(xié)議。HTTP協(xié)議是一種分布式的面向資源的網(wǎng)絡(luò)應(yīng)用層協(xié)議,無(wú)論是服務(wù)器端提供Web服務(wù),還是客戶端消費(fèi)Web服務(wù)都非常簡(jiǎn)單。再加上瀏覽器、Javascript、AJAX、JSON以及HTML5等技術(shù)和工具的發(fā)展,互聯(lián)網(wǎng)應(yīng)用架構(gòu)設(shè)計(jì)表現(xiàn)出了從傳統(tǒng)的PHP、JSP、ASP.NET等服務(wù)器端動(dòng)態(tài)網(wǎng)頁(yè)向Web API + RIA(富互聯(lián)網(wǎng)應(yīng)用)過(guò)渡的趨勢(shì)。Web API專注于提供業(yè)務(wù)服務(wù),RIA專注于用戶界面和交互設(shè)計(jì),從此兩個(gè)領(lǐng)域的分工更加明晰。在這種趨勢(shì)下,Web API設(shè)計(jì)將成為服務(wù)器端程序員的必修課。然而,正如簡(jiǎn)單的Java語(yǔ)言并不意味著高質(zhì)量的Java程序,簡(jiǎn)單的HTTP協(xié)議也不意味著高質(zhì)量的Web API。要想設(shè)計(jì)出高質(zhì)量的Web API,還需要深入理解分布式系統(tǒng)及HTTP協(xié)議的特性。

冪等性定義

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

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方法的冪等性是指一次和多次請(qǐng)求某一個(gè)資源應(yīng)該具有同樣的副作用。冪等性屬于語(yǔ)義范疇,正如編譯器只能幫助檢查語(yǔ)法錯(cuò)誤一樣,HTTP規(guī)范也沒(méi)有辦法通過(guò)消息格式等語(yǔ)法手段來(lái)定義它,這可能是它不太受到重視的原因之一。但實(shí)際上,冪等性是分布式系統(tǒng)設(shè)計(jì)中十分重要的概念,而HTTP的分布式本質(zhì)也決定了它在HTTP中具有重要地位。

分布式事務(wù) vs 冪等設(shè)計(jì)

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

bool withdraw(account_id, amount)

withdraw的語(yǔ)義是從account_id對(duì)應(yīng)的賬戶中扣除amount數(shù)額的錢;如果扣除成功則返回true,賬戶余額減少amount;如果扣除失敗則返回false,賬戶余額不變。值得注意的是:和本地環(huán)境相比,我們不能輕易假設(shè)分布式環(huán)境的可靠性。一種典型的情況是withdraw請(qǐng)求已經(jīng)被服務(wù)器端正確處理,但服務(wù)器端的返回結(jié)果由于網(wǎng)絡(luò)等原因被掉丟了,導(dǎo)致客戶端無(wú)法得知處理結(jié)果。如果是在網(wǎng)頁(yè)上,一些不恰當(dāng)?shù)脑O(shè)計(jì)可能會(huì)使用戶認(rèn)為上一次操作失敗了,然后刷新頁(yè)面,這就導(dǎo)致了withdraw被調(diào)用兩次,賬戶也被多扣了一次錢。如圖1所示:



這個(gè)問(wèn)題的解決方案一是采用分布式事務(wù),通過(guò)引入支持分布式事務(wù)的中間件來(lái)保證withdraw功能的事務(wù)性。分布式事務(wù)的優(yōu)點(diǎn)是對(duì)于調(diào)用者很簡(jiǎn)單,復(fù)雜性都交給了中間件來(lái)管理。缺點(diǎn)則是一方面架構(gòu)太重量級(jí),容易被綁在特定的中間件上,不利于異構(gòu)系統(tǒng)的集成;另一方面分布式事務(wù)雖然能保證事務(wù)的ACID性質(zhì),而但卻無(wú)法提供性能和可用性的保證。

另一種更輕量級(jí)的解決方案是冪等設(shè)計(jì)。我們可以通過(guò)一些技巧把withdraw變成冪等的,比如:

int create_ticket() 
bool idempotent_withdraw(ticket_id, account_id, amount)

create_ticket的語(yǔ)義是獲取一個(gè)服務(wù)器端生成的唯一的處理號(hào)ticket_id,它將用于標(biāo)識(shí)后續(xù)的操作。idempotent_withdraw和withdraw的區(qū)別在于關(guān)聯(lián)了一個(gè)ticket_id,一個(gè)ticket_id表示的操作至多只會(huì)被處理一次,每次調(diào)用都將返回第一次調(diào)用時(shí)的處理結(jié)果。這樣,idempotent_withdraw就符合冪等性了,客戶端就可以放心地多次調(diào)用。

基于冪等性的解決方案中一個(gè)完整的取錢流程被分解成了兩個(gè)步驟:1.調(diào)用create_ticket()獲取ticket_id;2.調(diào)用idempotent_withdraw(ticket_id, account_id, amount)。雖然create_ticket不是冪等的,但在這種設(shè)計(jì)下,它對(duì)系統(tǒng)狀態(tài)的影響可以忽略,加上idempotent_withdraw是冪等的,所以任何一步由于網(wǎng)絡(luò)等原因失敗或超時(shí),客戶端都可以重試,直到獲得結(jié)果。如圖2所示:



和分布式事務(wù)相比,冪等設(shè)計(jì)的優(yōu)勢(shì)在于它的輕量級(jí),容易適應(yīng)異構(gòu)環(huán)境,以及性能和可用性方面。在某些性能要求比較高的應(yīng)用,冪等設(shè)計(jì)往往是唯一的選擇。

HTTP的冪等性

HTTP協(xié)議本身是一種面向資源的應(yīng)用層協(xié)議,但對(duì)HTTP協(xié)議的使用實(shí)際上存在著兩種不同的方式:一種是RESTful的,它把HTTP當(dāng)成應(yīng)用層協(xié)議,比較忠實(shí)地遵守了HTTP協(xié)議的各種規(guī)定;另一種是SOA的,它并沒(méi)有完全把HTTP當(dāng)成應(yīng)用層協(xié)議,而是把HTTP協(xié)議作為了傳輸層協(xié)議,然后在HTTP之上建立了自己的應(yīng)用層協(xié)議。本文所討論的HTTP冪等性主要針對(duì)RESTful風(fēng)格的,不過(guò)正如上一節(jié)所看到的那樣,冪等性并不屬于特定的協(xié)議,它是分布式系統(tǒng)的一種特性;所以,不論是SOA還是RESTful的Web API設(shè)計(jì)都應(yīng)該考慮冪等性。下面將介紹HTTP GET、DELETE、PUT、POST四種主要方法的語(yǔ)義和冪等性。

HTTP GET方法用于獲取資源,不應(yīng)有副作用,所以是冪等的。比如:GET http://www.bank.com/account/123456,不會(huì)改變資源的狀態(tài),不論調(diào)用一次還是N次都沒(méi)有副作用。請(qǐng)注意,這里強(qiáng)調(diào)的是一次和N次具有相同的副作用,而不是每次GET的結(jié)果相同。GET http://www.news.com/latest-news這個(gè)HTTP請(qǐng)求可能會(huì)每次得到不同的結(jié)果,但它本身并沒(méi)有產(chǎn)生任何副作用,因而是滿足冪等性的。

HTTP DELETE方法用于刪除資源,有副作用,但它應(yīng)該滿足冪等性。比如:DELETE http://www.forum.com/article/4231,調(diào)用一次和N次對(duì)系統(tǒng)產(chǎn)生的副作用是相同的,即刪掉id為4231的帖子;因此,調(diào)用者可以多次調(diào)用或刷新頁(yè)面而不必?fù)?dān)心引起錯(cuò)誤。

比較容易混淆的是HTTP POST和PUT。POST和PUT的區(qū)別容易被簡(jiǎn)單地誤認(rèn)為“POST表示創(chuàng)建資源,PUT表示更新資源”;而實(shí)際上,二者均可用于創(chuàng)建資源,更為本質(zhì)的差別是在冪等性方面。在HTTP規(guī)范中對(duì)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所對(duì)應(yīng)的URI并非創(chuàng)建的資源本身,而是資源的接收者。比如:POST http://www.forum.com/articles的語(yǔ)義是在http://www.forum.com/articles下創(chuàng)建一篇帖子,HTTP響應(yīng)中應(yīng)包含帖子的創(chuàng)建狀態(tài)以及帖子的URI。兩次相同的POST請(qǐng)求會(huì)在服務(wù)器端創(chuàng)建兩份資源,它們具有不同的URI;所以,POST方法不具備冪等性。而PUT所對(duì)應(yīng)的URI是要?jiǎng)?chuàng)建或更新的資源本身。比如:PUT http://www.forum/articles/4231的語(yǔ)義是創(chuàng)建或更新ID為4231的帖子。對(duì)同一URI進(jìn)行多次PUT的副作用和一次PUT是相同的;因此,PUT方法具有冪等性。

在介紹了幾種操作的語(yǔ)義和冪等性之后,我們來(lái)看看如何通過(guò)Web API的形式實(shí)現(xiàn)前面所提到的取款功能。很簡(jiǎn)單,用POST /tickets來(lái)實(shí)現(xiàn)create_ticket;用PUT /accounts/account_id/ticket_id&amount=xxx來(lái)實(shí)現(xiàn)idempotent_withdraw。值得注意的是嚴(yán)格來(lái)講amount參數(shù)不應(yīng)該作為URI的一部分,真正的URI應(yīng)該是/accounts/account_id/ticket_id,而amount應(yīng)該放在請(qǐng)求的body中。這種模式可以應(yīng)用于很多場(chǎng)合,比如:論壇網(wǎng)站中防止意外的重復(fù)發(fā)帖。

總結(jié):

上面簡(jiǎn)單介紹了冪等性的概念,用冪等設(shè)計(jì)取代分布式事務(wù)的方法,以及HTTP主要方法的語(yǔ)義和冪等性特征。其實(shí),如果要追根溯源,冪等性是數(shù)學(xué)中的一個(gè)概念,表達(dá)的是N次變換與1次變換的結(jié)果相同,有興趣的讀者可以從Wikipedia上進(jìn)一步了解。

參考文獻(xiàn)
RFC 2616, Hypertext Transfer Protocol -- HTTP/1.1, Method Definitions
The Importance of Idempotence
Stackoverflow - PUT vs POST in REST

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書(shū)系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌,老刑警劉巖,帶你破解...
    沈念sama閱讀 230,182評(píng)論 6 543
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場(chǎng)離奇詭異,居然都是意外死亡,警方通過(guò)查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 99,489評(píng)論 3 429
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái),“玉大人,你說(shuō)我怎么就攤上這事。” “怎么了?”我有些...
    開(kāi)封第一講書(shū)人閱讀 178,290評(píng)論 0 383
  • 文/不壞的土叔 我叫張陵,是天一觀的道長(zhǎng)。 經(jīng)常有香客問(wèn)我,道長(zhǎng),這世上最難降的妖魔是什么? 我笑而不...
    開(kāi)封第一講書(shū)人閱讀 63,776評(píng)論 1 317
  • 正文 為了忘掉前任,我火速辦了婚禮,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘。我一直安慰自己,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 72,510評(píng)論 6 412
  • 文/花漫 我一把揭開(kāi)白布。 她就那樣靜靜地躺著,像睡著了一般。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上,一...
    開(kāi)封第一講書(shū)人閱讀 55,866評(píng)論 1 328
  • 那天,我揣著相機(jī)與錄音,去河邊找鬼。 笑死,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播,決...
    沈念sama閱讀 43,860評(píng)論 3 447
  • 文/蒼蘭香墨 我猛地睜開(kāi)眼,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼!你這毒婦竟也來(lái)了?” 一聲冷哼從身側(cè)響起,我...
    開(kāi)封第一講書(shū)人閱讀 43,036評(píng)論 0 290
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤,失蹤者是張志新(化名)和其女友劉穎,沒(méi)想到半個(gè)月后,有當(dāng)?shù)厝嗽跇?shù)林里發(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 49,585評(píng)論 1 336
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 41,331評(píng)論 3 358
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 43,536評(píng)論 1 374
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情,我是刑警寧澤,帶...
    沈念sama閱讀 39,058評(píng)論 5 363
  • 正文 年R本政府宣布,位于F島的核電站,受9級(jí)特大地震影響,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 44,754評(píng)論 3 349
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧,春花似錦、人聲如沸。這莊子的主人今日做“春日...
    開(kāi)封第一講書(shū)人閱讀 35,154評(píng)論 0 28
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)。三九已至,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背。 一陣腳步聲響...
    開(kāi)封第一講書(shū)人閱讀 36,469評(píng)論 1 295
  • 我被黑心中介騙來(lái)泰國(guó)打工, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 52,273評(píng)論 3 399
  • 正文 我出身青樓,卻偏偏與公主長(zhǎng)得像,于是被迫代替她去往敵國(guó)和親。 傳聞我的和親對(duì)象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 48,505評(píng)論 2 379

推薦閱讀更多精彩內(nèi)容

  • 理解HTTP冪等性 基于HTTP協(xié)議的Web API是時(shí)下最為流行的一種分布式服務(wù)提供方式。無(wú)論是在大型互聯(lián)網(wǎng)應(yīng)用...
    阿燈_supwinr閱讀 447評(píng)論 0 0
  • 本文轉(zhuǎn)自:Todd Wei 理解HTTP冪等性基于HTTP協(xié)議的Web API是時(shí)下最為流行的一種分布式服務(wù)提供方...
    i云哲閱讀 1,766評(píng)論 0 6
  • 如果要追根溯源,冪等性是數(shù)學(xué)中的一個(gè)概念,表達(dá)的是N次變換與1次變換的結(jié)果相同。 基于HTTP協(xié)議的Web API...
    Ray雷磊閱讀 1,903評(píng)論 0 1
  • 前言 HTTP Method的歷史: HTTP 0.9 這個(gè)版本只有GET方法 HTTP 1.0 這個(gè)版本有G...
    老馬的春天閱讀 25,990評(píng)論 3 9
  • 戀歸遠(yuǎn)方 昏渡的帆船 千陽(yáng)里 容不下一般孤苦 我已沒(méi)了溫度 該走向何處,惶恐而無(wú)助 覓見(jiàn)聚散情霧 夢(mèng)魂不覺(jué)至處 到...
    隱江CJJT閱讀 185評(píng)論 0 5