如今微服務和分布式架構變的越來越流行,而簡單,可靠,高效,跨平臺和跨語言的 Web Service 則是這類系統架構的基石。 RESTful Web Service 恰好滿足這些特點,被越來越多的系統架構所采用。
本文主要面向對 Web Service 有一定理解,需要進一步了解基于 REST 形式的 Web Service 的 IT 開發人員和架構師。它不是 Web Service 入門介紹,你需要較多相關領域的知識背景才能理解全部內容。
什么是 RESTful Web Service
作為互聯網應用開發人員,我們經常能看到 Web Service,REST 和 RESTful Web Service 之類的描述,可我們真的清楚這些概念嗎?
Web Service 簡單來說是指提供給不同設備通過互聯網(一般使用 HTTP 協議)進行通信和交換數據的一種服務。RESTful Web Service 是實現 Web Service 的一種方式。那么到底什么是 RESTful Web Service呢?什么又是 REST 呢?
REST 和 RESTful Web Service
REST (Representational State Transfer) 是由美國計算機科學家 Roy Fielding在2000年的博士論文提出的一種架構方式。Roy Fielding絕對可以稱之為業界大牛,他現任 Adobe 首席科學家,是HTTP協議的首要作者之一,也是Apache項目的聯合創始人。
REST 是一種架構方式和約定,和具體實現無關,也不一定必須基于Web。我們一般把采用 REST 架構的 Web Service 稱之 RESTful Web Service。在實際項目應用中,嚴格來說,我們應該稱這種 Web Service 為具有 REST 風格的 Web Service。原因是我們在處理和解決某些實際問題時,這種 Service 可能并不完全嚴格遵守 REST 架構的所有必要約定。
RESTful Web Service 和基于SOAP的 Web Services 有著本質的不同。使用 SOAP 的 Web Service 實際上是以協議(protocol)的形式工作的。因為 SOAP Web Service 嚴格規定了如何發現和描述 API,其傳輸的消息也有嚴格統一的格式(例如傳輸的載體XML有嚴格的格式規范)。而 RESTful Web Service 并不是協議,它沒有規定傳輸消息的具體格式,它只是一種約定使用 REST 架構實現的 Web Service。RESTful Web Service 相比SOAP Web Service 更加簡單和輕量級。現在大部分 RESTful Web Service 都使用類似的形式,例如都使用HTTP傳輸,使用風格類似的 URL 作為 API 和 使用 JSON 或者 XML 來傳輸數據等等(目前 JSON 占據主導地位,并且有持續流行的趨勢[1])。
如果你之前沒有接觸過SOAP,你只需記住RESTful Web Service 是一種采用 REST 架構約定,更簡單,更輕量級的Web Service
REST 之父大牛 Roy Fielding 在他的論文中,一共從下面6個方面闡述了 REST 架構的約定(REST 應該滿足哪些條件,以及這樣做會有什么好處)
REST 架構約定
CS結構(Client–server)
客戶端是一個相對獨立的實現,它不必考慮數據的持久化存儲問題。服務端擁有和保存數據,服務端不去關心客戶端內部實現,也不用關心客戶端請求的上下文。服務端和客戶端之間遵守相同的接口規范。在遵守相同接口規范的前提下,二者都可以獨立演化,甚至可以被其它的實現替代。-
無狀態(Stateless)
任何時候一個客戶端的請求數據都包含能夠讓服務端完成請求的充分信息,服務端不依賴前后不同請求的順序和狀態信息來完成請求。請求的session信息由客戶端持有,并在必要時連同請求數據一起發送。服務端可以使用請求中的session信息去其它外部服務或者數據庫獲取相關內容進以便對該請求做權限驗證等操作。如果所需數據要通過多次請求才能完成,客戶端必須自己負責記錄狀態(因為服務器不跟蹤客戶端的狀態),在下次請求時附帶發出。一個典型的例子是分頁的實現,客戶端需要自己保存當前頁數,請求下一頁時作為參數一起發給服務端,服務端使用該參數返回正確的下一頁數據。有些設計,比如Facebook API,服務端返回數據中包含下一頁數據的請求URL,客戶端只要記錄這個URL即可發起下一頁的請求。
服務端不依賴客戶端的請求順序和狀態提高了服務器的可擴展性(scalability)。比如在使用Load balancer的情況下,不能因為某個請求被分配到其它服務器而丟失某些信息從而返回不正確的數據。
無狀態的約定也提高了系統的健壯性(reliability)。如果集群服務器中的其中一臺發生故障也不會對系統的平穩運行造成太大影響。不過無狀態的約定也是有缺點的,客戶端必須每次都要帶上相同重復的信息來確定自己的身份和狀態,這就造成了傳輸數據的冗余性。然而沒有一個架構決策是十全十美的,最終的決策往往都是相互妥協的結果,我們只能選擇一個相對有優勢的決策。
緩存機制(Cacheable)
服務端應該明確規定返回數據的緩存機制,包括是否可緩存,緩存如何失效以及利用緩存獲取增量數據而不必每次獲取全部數據等。合理的緩存設計可以減少請求次數,進而提高服務器的效率和性能。系統分層(Layered system)
客戶端不用知道數據是從服務端直接返回還是通過中轉代理返回。這樣的設計也同樣提高了系統的可擴展性。比如可以使用負責均衡和反向代理等技術來對系統進行水平擴展和緩存處理,把系統劃分成不同的層次。使用分層設計也方便我們管理不同資源的權限,有利于提高系統的安全性。可定制代碼(可選)(Code on demand)
服務端可選擇臨時給客戶端下發一些功能代碼讓客戶端來執行,從而定制和擴展客戶端的某些功能。比如服務端可以返回一些 Javascript 代碼讓客戶端執行,去實現某些特定的功能。-
一致的接口(Uniform interface)
一致的接口對 REST 服務至關重要。基于統一的接口規則可以簡化系統實現,降低子系統之間的耦合度,因為子系統只要關注實現接口即可。在保證接口一致的情況下,不同的實現可以各自獨立演化。對于接口的要求有這么四個方面:一致的數據格式
雖然服務端內部不同數據的存儲格式可能千差萬別,但返回給客戶端的數據一定要有一個統一的表現形式。比如 Web Service 請求返回格式要么是 HTML,要么是 XML,要么是 JSON,不能返回服務端自己內部使用的特殊格式。可以對已有數據進一步操作(Resource Identifiers)
如果客戶端擁有一個資源,必要時,客戶端應該擁有足有的信息去修改和刪除這個資源。通常我們只要在返回的數據中包含一個 UID 即可做到這點。比如從服務端獲得了一個訂單數據,這個訂單數據里應該保證有一個唯一的訂單 ID,當我們想對這個訂單進行進一步操作時,可以保障操作的是同一個訂單。數據具有自我描述性
每項數據應該是可以自我描述的,方便代碼去處理和解析其中的內容。比如通過HTTP返回的數據里面有 [MIME type ]信息,我們從MIME type里面可以知道數據的具體格式,是圖片,視頻還是JSON。應用系統狀態變化只依賴超媒體(Hypermedia)
應用系統狀態變化只依賴于服務端發來的Hypermedia(如超鏈接 hyperlinks)。舉例來說,假設向一個微博 Web Service 請求一條微博信息,服務端響應信息中應該包含和這條微博相關的其它的URL。客戶端可以進一步利用這些URL發起請求來獲取感興趣的信息。前面章節中提到的Facebook API 可以從第一頁的返回數據中獲取下一頁的URL也是基于這個設計理念。
REST 的這些約定為我們設計 RESTful Web Service 提供了一個絕佳的范本。套用這個范本,在設計 Web Service 時,可以在一定程度上避免很多問題和少走彎路。因為這些約定本質上都是對現實系統設計優點的一些提煉和總結,它已經比較周全的為我們考慮了可能出現的問題,并且提供了解決問題的基本原則和方向。
本文第三小節“REST 架構約定”的主要內容來自對參考文檔中的部分內容的整理和翻譯。計算機科學有很多非常有價值的論文,很多論文是如此的重要以至于能奠定一個時代的技術基礎(比如 Google 的 MapReduce,可能需自備梯子)。我們只有充分理解了其中的思想和原則才能作出更好的設計和架構。
下一篇文章我們將結合HTTP的特性,探討如何設計 RESTful Web Service API
參考文檔
-
這個結論來自Goolge Trends(須自備梯子): https://www.google.com/trends/explore?date=all&q=xml%20api,json%20api ?