REST是Roy Fielding博士在他的畢業(yè)論文中首先提出來(lái)的。Roy Fielding何許人也?他是HTTP協(xié)議的主要設(shè)計(jì)者溉浙,是Apache Web Server的作者,是Apache軟件基金會(huì)的聯(lián)合創(chuàng)始人。所以他的論文一問(wèn)世颅湘,就引起了業(yè)界普遍的重視。在這篇論文里栗精,他總結(jié)了Web闯参,作為全世界最大的分布式系統(tǒng),之所以能成功的經(jīng)驗(yàn)悲立,也就是REST架構(gòu)風(fēng)格鹿寨。
時(shí)至今日,REST這個(gè)術(shù)語(yǔ)被廣泛使用薪夕。要是你到現(xiàn)在還沒(méi)聽(tīng)過(guò)REST脚草,你可以去一邊兒rest去了……不過(guò),雖然REST這個(gè)術(shù)語(yǔ)被廣泛使用原献,但是很多人對(duì)其的了解都是很模糊的馏慨。本文從REST架構(gòu)風(fēng)格的起源、REST的特點(diǎn)姑隅、REST實(shí)現(xiàn)要點(diǎn)等多個(gè)方面写隶,來(lái)探討一下,究竟REST為何物讲仰。
一慕趴、什么是架構(gòu)風(fēng)格
要了解“REST架構(gòu)風(fēng)格”,我們得先知道什么是"架構(gòu)風(fēng)格"鄙陡。
說(shuō)到風(fēng)格冕房,先來(lái)看看我們熟悉的服裝風(fēng)格:
是什么形成了這些服裝風(fēng)格,讓人一眼就能分辨出它們呢趁矾?——這是因?yàn)檫@些服裝的設(shè)計(jì)滿足了一組設(shè)計(jì)約束毒费。比如“韓版”色彩要清新、“嘻哈”造型要寬大愈魏、“朋克”要黑色加釘子質(zhì)感觅玻。滿足了這些約束想际,就會(huì)形成服裝風(fēng)格,同時(shí)也具備這些風(fēng)格所帶來(lái)的特點(diǎn)溪厘,比如“韓版”潮胡本、“嘻哈”隨性、“朋克”酷……
類似地畸悬,架構(gòu)風(fēng)格就是一組架構(gòu)的約束侧甫,滿足了這些約束,就可以具備一系列架構(gòu)的屬性蹋宦,比如可伸縮性披粟、簡(jiǎn)單性、可移植性等等冷冗。架構(gòu)風(fēng)格有很多種守屉,Roy Fielding在他的論文里列舉了二十多種。這里蒿辙,我們列舉對(duì)我們理解REST有幫助的幾個(gè)架構(gòu)風(fēng)格:
客戶-服務(wù)器風(fēng)格(Client-Server拇泛,CS):這種風(fēng)格是基于網(wǎng)絡(luò)的應(yīng)用最常見(jiàn)的一種架構(gòu)風(fēng)格∷脊啵客戶端發(fā)送請(qǐng)求給服務(wù)器俺叭,服務(wù)器處理請(qǐng)求并將結(jié)果返回給客戶端。這種架構(gòu)風(fēng)格約束要求分離關(guān)注點(diǎn)泰偿。功能的適當(dāng)分離會(huì)簡(jiǎn)化服務(wù)器組件熄守,從而提高可伸縮性。這種分離也使得服務(wù)器和客戶端可以獨(dú)立演進(jìn)耗跛。
分層系統(tǒng)風(fēng)格(Layered System裕照,LS):一個(gè)分層系統(tǒng)是按照層次來(lái)組織的,每一層為在其之上的層提供服務(wù)课兄,層與層之間不能跨層訪問(wèn)(層隔離原則)牍氛。每層的關(guān)注點(diǎn)分離晨继,這樣使得層與層之間解耦烟阐,每層的組件可以獨(dú)立演進(jìn)。
不同的架構(gòu)風(fēng)格還可以組合在一起紊扬,從而生成新的風(fēng)格蜒茄。比如:
分層-客戶-服務(wù)器風(fēng)格(LayeredClient-Server,LCS):這種風(fēng)格在CS風(fēng)格的基礎(chǔ)上添加了代理(proxy)組件層和網(wǎng)關(guān)(gateway)組件層餐屎。這些額外的中間層檀葛,為系統(tǒng)添加了負(fù)載均衡、安全性檢查等功能腹缩。
客戶-無(wú)狀態(tài)-服務(wù)器風(fēng)格(Client-Stateless-Server屿聋,CSS):這種風(fēng)格在CS風(fēng)格的基礎(chǔ)添加了新的約束:服務(wù)器不允許有會(huì)話狀態(tài)空扎。從客戶端發(fā)到服務(wù)器的每個(gè)請(qǐng)求必須包含理解請(qǐng)求所需的全部信息,不能用任何保存在服務(wù)器上的上下文信息润讥。這個(gè)風(fēng)格使得系統(tǒng)具有很好的可伸縮性转锈,因?yàn)榉?wù)器不用保存多個(gè)請(qǐng)求之間的狀態(tài),請(qǐng)求可以分發(fā)到不同的服務(wù)器中進(jìn)行處理楚殿。
緩存風(fēng)格($):緩存?zhèn)€別請(qǐng)求的結(jié)果撮慨,以便可以被后面的請(qǐng)求重用。緩存風(fēng)格可以提高性能脆粥,尤其是在基于網(wǎng)絡(luò)的系統(tǒng)中砌溺。
按需代碼風(fēng)格(Code On Demand,COD):這種風(fēng)格中变隔,客戶端向服務(wù)器發(fā)送請(qǐng)求规伐,服務(wù)器會(huì)根據(jù)需要把相應(yīng)的代碼發(fā)給客戶端,然后客戶端在本地執(zhí)行這些代碼弟胀。舉個(gè)例子楷力,比如瀏覽器訪問(wèn)一個(gè)網(wǎng)站,網(wǎng)站會(huì)把js腳本發(fā)給瀏覽器孵户,瀏覽器在本地執(zhí)行這些js腳本萧朝,對(duì)網(wǎng)頁(yè)進(jìn)行渲染。按需代碼的優(yōu)點(diǎn)有:能夠?yàn)橐粋€(gè)已部署的客戶端添加功能夏哭,改善了可擴(kuò)展性和可配置性检柬;當(dāng)代碼在本地與用戶交互而不是通過(guò)遠(yuǎn)程交互時(shí),能夠得到更好的用戶可覺(jué)察性能和效率竖配;由于服務(wù)器將工作交給了客戶端何址,從而改善了服務(wù)器的可伸縮性。它的缺點(diǎn)也很明顯:它會(huì)更加復(fù)雜性进胯;在系統(tǒng)安全性方面也面臨挑戰(zhàn)用爪;如果客戶端無(wú)法信任服務(wù)器,還會(huì)帶來(lái)部署問(wèn)題胁镐。
講了這么多偎血,究竟架構(gòu)風(fēng)格有什么用呢?
可以通過(guò)架構(gòu)風(fēng)格來(lái)比較分析各種不同架構(gòu)的特點(diǎn)盯漂。因?yàn)橐环N具體架構(gòu)既包含功能屬性又包含非功能屬性颇玷,直接比較不同類型系統(tǒng)的架構(gòu)會(huì)比較困難。而架構(gòu)風(fēng)格通過(guò)忽略架構(gòu)中其余部分的偶然性細(xì)節(jié)就缆,捕獲了一種交互模式的本質(zhì)特征帖渠,更容易對(duì)不同架構(gòu)進(jìn)行對(duì)比分析。
可以指導(dǎo)具體架構(gòu)設(shè)計(jì)竭宰。一種具體的架構(gòu)其實(shí)就是特定風(fēng)格的一個(gè)實(shí)例空郊。軟件架構(gòu)設(shè)計(jì)要滿足應(yīng)用的一系列需求份招,而為滿足這些需求,就要求架構(gòu)應(yīng)該必須具有一系列的屬性(比如可伸縮性狞甚、網(wǎng)絡(luò)效率等)脾还。而架構(gòu)風(fēng)格對(duì)架構(gòu)的約束,使得架構(gòu)具備了相應(yīng)的屬性入愧,可以對(duì)具體的架構(gòu)設(shè)計(jì)提供指導(dǎo)鄙漏。
可以推導(dǎo)出新的架構(gòu)風(fēng)格。通過(guò)將多種基本風(fēng)格組合為互相協(xié)作的一個(gè)整體棺蛛,可以推導(dǎo)出新的架構(gòu)風(fēng)格怔蚌。這種新的架構(gòu)風(fēng)格會(huì)繼承基礎(chǔ)風(fēng)格的一系列架構(gòu)屬性,形成自己獨(dú)有的架構(gòu)屬性旁赊。進(jìn)而可以用新的風(fēng)格指導(dǎo)架構(gòu)設(shè)計(jì)桦踊。
二、什么是REST架構(gòu)風(fēng)格
REST架構(gòu)風(fēng)格是Roy Fielding博士總結(jié)的Web架構(gòu)背后的設(shè)計(jì)基本原理终畅。它可以由一系列基礎(chǔ)架構(gòu)風(fēng)格推導(dǎo)出來(lái)的挟阻。從空風(fēng)格開(kāi)始蒙保,通過(guò)逐步增加約束(組合基礎(chǔ)架構(gòu)風(fēng)格),最終形成REST架構(gòu)風(fēng)格,其推導(dǎo)過(guò)程如下:
推導(dǎo)過(guò)程主要用到了我們上一節(jié)所列舉的基礎(chǔ)架構(gòu)風(fēng)格赌蔑。綜合這些基礎(chǔ)架構(gòu)風(fēng)格的約束莫换,總的來(lái)說(shuō)乌奇,REST架構(gòu)風(fēng)格的約束主要有6個(gè):
- 客戶-服務(wù)器:通信只能由客戶端單方面發(fā)起驶鹉,表現(xiàn)為請(qǐng)求 - 響應(yīng)的形式。
- 無(wú)狀態(tài):通信的會(huì)話狀態(tài)(Session State)應(yīng)該全部由客戶端負(fù)責(zé)維護(hù)絮识。
- 分層系統(tǒng):通過(guò)限制組件的行為(即绿聘,每個(gè)組件只能“看到”與其交互的緊鄰層),將架構(gòu)分解為若干等級(jí)的層次舌。
- 緩存:響應(yīng)內(nèi)容可以在通信鏈的某處被緩存熄攘,以改善網(wǎng)絡(luò)效率。
- 統(tǒng)一接口:通信鏈的組件之間通過(guò)統(tǒng)一的接口相互通信彼念,整體的架構(gòu)得到了簡(jiǎn)化挪圾,交互的可見(jiàn)性也得到了改善。
- 按需代碼(可選):支持通過(guò)下載并執(zhí)行一些代碼(例如 Java Applet国拇、Flash 或 JavaScript)洛史,對(duì)客戶端的功能進(jìn)行擴(kuò)展惯殊。
其中酱吝,強(qiáng)調(diào)組件之間要有一個(gè)統(tǒng)一的接口,是REST架構(gòu)風(fēng)格區(qū)別于其他基于網(wǎng)絡(luò)的架構(gòu)風(fēng)格的核心特征土思。通過(guò)統(tǒng)一接口务热,整體的系統(tǒng)架構(gòu)得到了簡(jiǎn)化忆嗜,交互的可見(jiàn)性也得到了改善。實(shí)現(xiàn)與它們所提供的服務(wù)是解耦的崎岂,這促進(jìn)了 獨(dú)立的可進(jìn)化性捆毫。然而,付出的代價(jià)是冲甘,統(tǒng)一接口降低了效率绩卤,因?yàn)樾畔⒍际褂脴?biāo)準(zhǔn)化的形式來(lái)轉(zhuǎn)移,而不能使用特定于應(yīng)用需求的形式江醇。
由于遵循了這些約束濒憋,REST架構(gòu)風(fēng)格就獲得了相應(yīng)的架構(gòu)屬性:
- 可伸縮性:通過(guò)客戶-服務(wù)器、分層系統(tǒng)陶夜、無(wú)狀態(tài)等約束凛驮,使得系統(tǒng)獲得了很好的可伸縮性。
- 簡(jiǎn)單性:通過(guò)客戶-服務(wù)器条辟、分層系統(tǒng)黔夭、統(tǒng)一接口等約束,分離關(guān)注點(diǎn)羽嫡,使得系統(tǒng)更容易被理解和實(shí)現(xiàn)本姥。
- 可修改性:通過(guò)客戶-服務(wù)器、分層系統(tǒng)杭棵、統(tǒng)一接口扣草、按需代碼等約束,使得系統(tǒng)獲得了獨(dú)立進(jìn)化颜屠、動(dòng)態(tài)擴(kuò)展辰妙、可重用的能力。
- 網(wǎng)絡(luò)效率:關(guān)于基于網(wǎng)絡(luò)的應(yīng)用的一個(gè)有趣現(xiàn)象是甫窟,最佳的應(yīng)用性能就是不使用網(wǎng)絡(luò)密浑。通過(guò)緩存、按需代碼等約束粗井,使得系統(tǒng)可以盡可能減少網(wǎng)絡(luò)訪問(wèn)尔破,提升網(wǎng)絡(luò)效率,減少由其他約束(如無(wú)狀態(tài)浇衬、分層系統(tǒng))引入的網(wǎng)絡(luò)開(kāi)銷懒构。
三、REST架構(gòu)風(fēng)格的一個(gè)實(shí)例——Web架構(gòu)
從REST被提出的歷史也可以看出耘擂,其實(shí)現(xiàn)代互聯(lián)網(wǎng)Web架構(gòu)——這個(gè)世界上最大最成功的分布式應(yīng)用的架構(gòu)——就是REST架構(gòu)風(fēng)格的一個(gè)具體實(shí)例胆剧。
構(gòu)成Web架構(gòu)的組件有:
- 用戶代理:如Web瀏覽器。
- 來(lái)源服務(wù)器:如Apache httpd。是資源的權(quán)威來(lái)源秩霍。
- 代理:如Netscape代理篙悯。一個(gè)代理組件是由客戶端選擇的中間組件,用來(lái)為其他的服務(wù)铃绒、數(shù)據(jù)轉(zhuǎn)換鸽照、性能增強(qiáng)、或安全保護(hù)提供接口封裝颠悬。
- 網(wǎng)關(guān):也叫反向代理矮燎,如nginx。一個(gè)網(wǎng)關(guān)組件是由網(wǎng)絡(luò)或來(lái)源服務(wù)器強(qiáng)加的中間組件赔癌,用來(lái)為其他的服務(wù)漏峰、 數(shù)據(jù)轉(zhuǎn)換、性能增強(qiáng)届榄,或安全增強(qiáng)提供接口封裝浅乔。代理和網(wǎng)關(guān)之間的區(qū)別是,何時(shí)使用代理是由客戶端來(lái)決定的铝条。
為什么這些組件組合起來(lái)可以形成REST架構(gòu)風(fēng)格呢靖苇?這離不開(kāi)Web背后的三大基石:URI、HTTP班缰、超媒體贤壁。
1. URI作為資源的標(biāo)識(shí)
什么是資源?——任何能夠被命名的信息都能夠作為一個(gè)資源埠忘,比如一份文檔脾拆、一張圖片。一個(gè)資源是到一組實(shí)體概念上的映射莹妒,而不是實(shí)體本身名船。比如,“一份代碼的最新版本”這個(gè)資源旨怠,它的值是在不斷變化的渠驼;而“這份代碼的V1.0版本”這個(gè)資源,它的值是靜態(tài)的鉴腻。這兩個(gè)是截然不同的資源迷扇,即使某個(gè)時(shí)刻他們映射的實(shí)體是相同的(即V1.0版本就是最新版本)。URI就是這個(gè)資源的標(biāo)識(shí)爽哎。
對(duì)資源的這一抽象的定義使得 Web 架構(gòu)的核心功能得以實(shí)現(xiàn)蜓席。首先,它通過(guò)包含了很多信息來(lái)源而沒(méi)有人為地通過(guò)類型或?qū)崿F(xiàn)對(duì)它們加以區(qū)分课锌,從而實(shí)現(xiàn)了通用性厨内。其次,它允許引用到表述的延遲綁定,從而支持基于請(qǐng)求的性質(zhì)來(lái)進(jìn)行內(nèi)容協(xié)商隘庄。最后,它允許一個(gè)創(chuàng)作者引用一個(gè)概念而不是引用此概念的某個(gè)單獨(dú)的表述癣亚,從而使得當(dāng)表述改變時(shí)無(wú)須修改所有的現(xiàn)有鏈接(假設(shè)創(chuàng)作者使用了正確的標(biāo)識(shí)符)丑掺。
2. HTTP作為統(tǒng)一的接口
有了資源的抽象,就可以對(duì)資源的操作設(shè)定具有統(tǒng)一語(yǔ)義的接口述雾,這就是HTTP的使命街州。HTTP的統(tǒng)一接口表現(xiàn)在幾個(gè)方面:
統(tǒng)一的方法:
GET: 用于獲取資源。它既是冪等的玻孟,又是安全無(wú)副作用的唆缴。
POST: 用于請(qǐng)求服務(wù)端創(chuàng)建一個(gè)資源。它即不是冪等的黍翎,也不是安全的面徽。
PUT:用于客戶端創(chuàng)建或重寫(xiě)一個(gè)資源。它是冪等的匣掸,但不是安全的趟紊。
DELETE:用于刪除資源。它是冪等的碰酝,但不是安全的霎匈。統(tǒng)一的狀態(tài)碼,如:
200 (OK)- 如果已存在資源被更改
201 (created)- 如果新資源被創(chuàng)建
301(Moved Permanently)- 資源的URI已更改
400 (bad request)- 指代壞請(qǐng)求
404 (not found)- 資源不存在
500 (internal server error)- 通用錯(cuò)誤響應(yīng)
503 (Service Unavailable)- 服務(wù)當(dāng)前無(wú)法處理請(qǐng)求統(tǒng)一的控制信息送爸,如:
Header中的Cache-Control用來(lái)控制緩存策略
Header中的Connection用來(lái)控制是否持久連接
有了HTTP這套統(tǒng)一語(yǔ)義的接口铛嘱,Web系統(tǒng)中的各個(gè)組件,可以不再關(guān)心具體的消息內(nèi)容而工作袭厂。比如緩存服務(wù)器可以識(shí)別是GET方法墨吓,根據(jù)其即安全又冪等的特性,默認(rèn)對(duì)其進(jìn)行緩存纹磺;并且可以根據(jù)Cache-Control中指定的信息肛真,來(lái)決定緩存的策略。這套統(tǒng)一語(yǔ)義的接口使得Web系統(tǒng)間組件相互解耦爽航,整個(gè)系統(tǒng)的集成變得簡(jiǎn)單蚓让。
3. 超媒體作為資源的表述和應(yīng)用狀態(tài)的引擎
一個(gè)表述,指的是某個(gè)資源在某個(gè)特定時(shí)刻的視圖讥珍。這個(gè)視圖被編碼為一種或多種可轉(zhuǎn)移的格式历极,比如HTML、XML衷佃、JSON趟卸、MP3、JPEG等。對(duì)于一個(gè)資源的訪問(wèn)锄列,總是通過(guò)其表述方式來(lái)間接完成的图云。
Web系統(tǒng)中以超媒體作為資源的表述。超媒體最大的特征是具有超鏈接邻邮。這一簡(jiǎn)單的特性竣况,卻可以使得客戶和服務(wù)端得到的極大解耦。想象這樣一個(gè)場(chǎng)景筒严,你在淘寶上購(gòu)買商品丹泉。這個(gè)過(guò)程是這樣的:
你會(huì)發(fā)現(xiàn),在這個(gè)過(guò)程中鸭蛙,你唯一需要知道的信息只有網(wǎng)站入口摹恨,而整個(gè)購(gòu)買過(guò)程的順利進(jìn)行,完全依賴于超鏈接給你的提示娶视,每進(jìn)入一步晒哄,超鏈接都會(huì)提示你后續(xù)還能做哪些操作。你不必記住每個(gè)操作具體的API肪获,只要你能明白超媒體的“語(yǔ)義”揩晴,你就能順利完成購(gòu)買。你需要記住的東西越少贪磺,你和網(wǎng)站之間的耦合就越弱硫兰。比如這個(gè)例子中,網(wǎng)站變更"將商品加入購(gòu)物車的API“對(duì)你來(lái)說(shuō)是透明的寒锚。
我們?cè)賮?lái)看看劫映,這個(gè)過(guò)程中,網(wǎng)站的狀態(tài)是如何變化的:
在這個(gè)過(guò)程中刹前,超媒體實(shí)際上充當(dāng)了網(wǎng)站(應(yīng)用)狀態(tài)遷移的引擎泳赋,它推動(dòng)著整個(gè)應(yīng)用狀態(tài)的遷移——與傳統(tǒng)狀態(tài)機(jī)的不同之處在于,客戶端預(yù)先不知道可能的狀態(tài)和遷移喇喉,當(dāng)應(yīng)用到達(dá)一個(gè)新的狀態(tài)時(shí)祖今,接下來(lái)的狀態(tài)遷移是可被發(fā)現(xiàn)的。就像是一個(gè)尋寶的過(guò)程拣技。
再開(kāi)一下腦洞千诬,如果把整個(gè)互聯(lián)網(wǎng)看成是一個(gè)分布式應(yīng)用,而“你”是個(gè)客戶端程序膏斤,那么只要你這個(gè)客戶端能理解超媒體中的“語(yǔ)義”徐绑,就可以通過(guò)選擇超鏈接的方式,將“超媒體作為應(yīng)用狀態(tài)的引擎”莫辨,探索式的完成各種業(yè)務(wù)流程傲茄。這里毅访,你這個(gè)客戶端和分布式應(yīng)用是非常松耦合的。
小結(jié)
可以說(shuō)盘榨,Web架構(gòu)之所以能實(shí)現(xiàn)REST架構(gòu)風(fēng)格喻粹,就是因?yàn)樯鲜鋈蠡K鼈內(nèi)齻€(gè)草巡,作為粘合劑把整個(gè)系統(tǒng)的各個(gè)組件聯(lián)在了一起守呜;同時(shí),作為整個(gè)系統(tǒng)的靈魂捷犹,使得Web系統(tǒng)具有了一系列優(yōu)越性弛饭,成為世界上最大最成功的分布式應(yīng)用冕末。理解了上述Web的三大基石萍歉,也就可以理解為什么這個(gè)架構(gòu)風(fēng)格要被命名為REST(REpresentation State Transfer,表述性狀態(tài)轉(zhuǎn)移)這么古怪的名字了档桃!它含義是:以超媒體作為資源的表述枪孩,通過(guò)HTTP將這些表述轉(zhuǎn)移到客戶端,而由客戶端通過(guò)服務(wù)器在超媒體中提供的可選超鏈接藻肄,來(lái)控制服務(wù)器上應(yīng)用狀態(tài)的遷移蔑舞。總結(jié)REST最為核心的就是:將超媒體作為應(yīng)用狀態(tài)的引擎(Hypermedia As The Engine Of Application State,縮寫(xiě)HATEOAS)嘹屯。
四攻询、把Web作為建造分布式系統(tǒng)的平臺(tái)。
你當(dāng)然可以自己按照REST架構(gòu)風(fēng)格的約束州弟,重新設(shè)計(jì)一個(gè)特定的架構(gòu)钧栖。然而,作為REST風(fēng)格的親生兒子婆翔,Web架構(gòu)拯杠,已經(jīng)是一個(gè)非常成熟的架構(gòu)體系了,包含非常多高可用的中間件啃奴√杜悖基于Web來(lái)建造分布式系統(tǒng),就相當(dāng)于站在了巨人的肩膀上最蕾,不但可以享受Web強(qiáng)大而豐富的基礎(chǔ)設(shè)施依溯,還可以獲得REST架構(gòu)風(fēng)格所帶來(lái)的一系列架構(gòu)優(yōu)點(diǎn)。這何樂(lè)而不為呢瘟则?因此誓沸,現(xiàn)有的RESTful架構(gòu)基本上都是基于Web的。
要想基于Web構(gòu)建一個(gè)REST架構(gòu)風(fēng)格的系統(tǒng)壹粟,除了需要滿足服務(wù)端無(wú)狀態(tài)的約束外拜隧,最核心的就是統(tǒng)一接口的約束宿百。這個(gè)“統(tǒng)一接口”也就是我們平常所說(shuō)的RESTful API。這也是為什么我們?cè)谡務(wù)揜EST架構(gòu)實(shí)現(xiàn)的時(shí)候洪添,很多時(shí)候?qū)嶋H上討論的是RESTful API的設(shè)計(jì)垦页。
然而很多人對(duì)RESTful API的內(nèi)涵并不清楚,有的以為用HTTP傳輸就是RESTful API干奢,有的以為用URL定義的API就是RESTful API……為了評(píng)判API滿足REST架構(gòu)風(fēng)格約束要求的程度痊焊,Richardson提出了REST成熟度模型:
第0級(jí):僅使用HTTP作為傳輸方式
其特征是整個(gè)服務(wù)只使用單個(gè)的URI,并且使用單個(gè)的HTTP方法(通常是POST)忿峻。比如基于SOAP規(guī)范的各種技術(shù)(WSDL薄啥、WS-Transfer等),它們僅使用HTTP來(lái)轉(zhuǎn)移SOAP的載荷逛尚,完全忽略了HTTP其他部分垄惧。
優(yōu)缺點(diǎn):不能利用Web架構(gòu)帶來(lái)的任何益處。
第1級(jí):使用URI描述資源
其使用了很多URI來(lái)標(biāo)識(shí)資源绰寞,但是只使用單個(gè)HTTP動(dòng)詞到逊。1級(jí)服務(wù)和0級(jí)服務(wù)的主要區(qū)別在于,1級(jí)服務(wù)暴露出了很多邏輯上的資源滤钱,而0級(jí)服務(wù)將所有的交互埋入了單個(gè)(大型的觉壶、復(fù)雜的)資源。在1級(jí)服務(wù)中件缸,通常會(huì)將操作名嵌入到URI中铜靶。如http://example.com/get_users
或者http://example.com/users?method=get
優(yōu)缺點(diǎn):不同的URI可以對(duì)資源進(jìn)行建模。但是由于只用一個(gè)HTTP動(dòng)詞(一般是POST)他炊,無(wú)法利用Web的緩存等中間件争剿。
第2級(jí):使用統(tǒng)一語(yǔ)意的HTTP動(dòng)詞和狀態(tài)碼
其使用了大量URI標(biāo)識(shí)資源,同時(shí)使用多個(gè)統(tǒng)一語(yǔ)義HTTP動(dòng)詞來(lái)操作資源:GET(讀扔映怼)秒梅、POST(創(chuàng)建)、PUT(更新)舌胶、DELETE(刪除)捆蜀,并且使用HTTP狀態(tài)碼來(lái)協(xié)調(diào)交互。其中幔嫂,合理使用GET具有重要的意義辆它。因?yàn)镚ET默認(rèn)是冪等并且安全的操作,這使得Web系統(tǒng)中的中間節(jié)點(diǎn)可以使用緩存機(jī)制緩存GET的結(jié)果履恩,該機(jī)制是讓目前Web運(yùn)轉(zhuǎn)如此良好的關(guān)鍵因素之一锰茉。
優(yōu)缺點(diǎn):可以利用Web的大部分基礎(chǔ)設(shè)施。由于HTTP動(dòng)詞對(duì)操作的冪等性和安全性做了嚴(yán)格的區(qū)分切心,準(zhǔn)確使用這些動(dòng)詞會(huì)使得系統(tǒng)更具健壯性飒筑。
第3級(jí):使用超媒體作為狀態(tài)轉(zhuǎn)移引擎
在第2級(jí)的基礎(chǔ)上片吊,此級(jí)服務(wù)支持“超媒體作為應(yīng)用狀態(tài)引擎”的理念。在表述中除了包含請(qǐng)求的數(shù)據(jù)协屡,還包含消費(fèi)者下一步可能感興趣的其他資源URI鏈接俏脊。超媒體控制的關(guān)鍵在于它告訴客戶端下一步能夠做什么。
優(yōu)缺點(diǎn):可以在保證客戶端不受影響的條件下肤晓,改變服務(wù)接口(通過(guò)超媒體返回的鏈接)爷贫,實(shí)現(xiàn)了客戶端和服務(wù)端的松耦合。
當(dāng)然补憾,客戶端要想理解返回的URI代表什么意思漫萄,還需要理解其“語(yǔ)義”∮遥可以通過(guò)構(gòu)建語(yǔ)義網(wǎng)絡(luò)腾务,使得客戶端根據(jù)返回的信息推斷出它的語(yǔ)義,這種方法比較復(fù)雜但可以使得客戶端和服務(wù)器足夠的解耦威酒;也可以通和客戶端事先約定好窑睁,這種方法相對(duì)簡(jiǎn)單挺峡,只是客戶端需要知道一些帶外信息葵孤,耦合性稍微高一點(diǎn)。
你們現(xiàn)在的RESTful API成熟度達(dá)到了第幾級(jí)呢橱赠?——就我觀察尤仍,我廠的API大部分還處在第1級(jí),或者第1級(jí)到第2級(jí)的路上……
小結(jié)
以Web為基礎(chǔ)狭姨,構(gòu)建REST服務(wù)宰啦,最重要的是RESTful API的設(shè)計(jì)。只有完全達(dá)到REST成熟度第2級(jí)饼拍,才能很好的利用Web架構(gòu)提供的基礎(chǔ)設(shè)施赡模;只有完全達(dá)到第3級(jí),實(shí)現(xiàn)超媒體控制师抄,才能享受REST架構(gòu)風(fēng)格帶來(lái)的好處漓柑,構(gòu)建出像Web一樣成功的分布式服務(wù)。(注:還有一個(gè)前提是叨吮,服務(wù)需要滿足無(wú)狀態(tài)的約束辆布。)
五、REST架構(gòu)的問(wèn)題
雖然REST架構(gòu)具有一系列優(yōu)點(diǎn)茶鉴,但是它也不是萬(wàn)能的锋玲,也有其自身的缺點(diǎn):
1. 統(tǒng)一接口帶來(lái)的性能損失:由于信息都使用標(biāo)準(zhǔn)化的形式來(lái)轉(zhuǎn)移,而不能使用特定于應(yīng)用需求的形式涵叮,網(wǎng)絡(luò)效率上會(huì)比較低惭蹂。
2. 緩存的存在伞插,使得一致性問(wèn)題更為突出:緩存機(jī)制可以彌補(bǔ)一些網(wǎng)絡(luò)效率。但這有引入了新問(wèn)題盾碗,客戶端獲得的數(shù)據(jù)可能是緩存了的舊數(shù)據(jù)蜂怎,而服務(wù)器又沒(méi)有主動(dòng)通知緩存更新的機(jī)制,這使得分布式系統(tǒng)的一致性問(wèn)題更加突出置尔。
除此之外杠步,由于目前REST的主要實(shí)現(xiàn)是基于HTTP的,HTTP的一些缺點(diǎn)也導(dǎo)致了一些問(wèn)題:
3. 請(qǐng)求響應(yīng)式交互榜轿,使得服務(wù)端無(wú)法通知客戶端:HTTP的設(shè)計(jì)使得服務(wù)端不具備向客戶端發(fā)起通知的能力幽歼。客戶端要想獲得最新?tīng)顟B(tài)谬盐,需要不停的向服務(wù)端發(fā)起請(qǐng)求甸私。這既浪費(fèi)網(wǎng)絡(luò)帶寬,也無(wú)法滿足高時(shí)效性的需求飞傀。
4. HTTP動(dòng)詞表達(dá)力還不足夠:目前的HTTP動(dòng)詞只能支持基本的CRUD操作皇型。并且某些情況下,用GET來(lái)獲取資源還受到URL長(zhǎng)度的限制砸烦。用這些動(dòng)詞描述完整的服務(wù)有時(shí)會(huì)有些力不從心弃鸦。目前已有一些提案,給HTTP增加一些動(dòng)詞幢痘,比如:SEARCH(搜索)唬格、INCLUDE(將資源加入到某個(gè)資源集合中并返回服務(wù)端設(shè)定的 URI)、PLACE(使用客戶端指定的 URI 向資源集合中添加資源)颜说、MERGE(通過(guò)提供的表述合并部分資源)等等购岗。
5. HTTP明文傳輸帶來(lái)的安全性問(wèn)題:HTTP使用明文傳輸,并且不驗(yàn)證報(bào)文的完整性门粪,使得報(bào)文很容被篡改喊积。而全棧使用HTTPS來(lái)解決安全性問(wèn)題,又會(huì)帶來(lái)較高的性能開(kāi)銷玄妈。
還是那句經(jīng)典:沒(méi)有銀彈乾吻。架構(gòu)設(shè)計(jì)的過(guò)程就是不停地權(quán)衡利弊得失,從中尋求一個(gè)最佳的平衡點(diǎn)的過(guò)程措近。REST對(duì)于有些場(chǎng)景可能是很好的溶弟,它的這些缺點(diǎn)影響并不大;而對(duì)于另一些應(yīng)用場(chǎng)景瞭郑,它的缺點(diǎn)可能就是不可接受的辜御。因此,在實(shí)際架構(gòu)設(shè)計(jì)過(guò)程中屈张,是否使用REST架構(gòu)擒权,還是要具體問(wèn)題具體分析袱巨,因地制宜。
參考:
- Roy Fielding博士論文《Architectural Styles and the Design of Network-based Software Architectures》
- Richardson Maturity Model
- 《REST In Practice》(REST實(shí)戰(zhàn))