1. 程式人生 > >架構風格:你真的懂REST嗎?

架構風格:你真的懂REST嗎?

本文探討如下幾個問題:

  • 什麼是REST
  • REST包含哪些約束
  • 什麼是RESTful
  • 純RESTful API的難點在哪裡

如果你去搜索「什麼是REST」的話,大部分情況下,你看到的基本都是RESTful!

這類內容主要說的是:

  • 資源URL應該怎麼寫
  • 要用GET來獲取資源
  • 要用POST來新建資源
  • 要用PUT來更新資源
  • 要用DELETE來刪除資源

而實際上REST並不是這些,或者說並不完全是這些!

什麼是REST

REST全稱Representational State Transfer,出自Roy Thomas Fielding博士的博士論文《Architectural Styles and the Design of Network-based Software Architectures》第五章(Fielding博士的這篇論文會在後面單獨討論),一般翻譯為「表述性狀態轉移」。

在論文的第6章第一節提到了為什麼會取REST這麼一個名字:

The name 「Representational State Transfer」 is intended to evoke an image of how a well-designed Web application behaves: a network of web pages (a virtual state-machine), where the user progresses through the application by selecting links (state transitions), resulting in the next page (representing the next state of the application) being transferred to the user and rendered for their use. 「表述性狀態轉移」這個名稱是為了喚起人們對於一個良好設計的 Web 應用如何運轉的印象:一個由網頁組成的網路(一個虛擬狀態機),使用者通過選擇連結在應用中前進(狀態遷移),導致下一個頁面(應用的下一個狀態的表述)被轉移給使用者,並且呈現給他們,以便他們來使用。

  • 架構風格是一組架構約束
  • REST是一種架構風格

所以,REST是一組架構約束

REST約束

REST是一個複合架構風格,即它包含了其它的架構風格!

REST約束包括:CS,無狀態,分層,快取,統一介面以及按需程式碼。其中「統一介面」是REST與其它架構風格的主要區別所在!「統一介面」包括了四個子約束:資源的識別,通過表述操作資源,自描述的訊息,超媒體作為應用狀態引擎!

《Architectural Styles and the Design of Network-based Software Architectures》第五章給出了REST的完整推導過程,這裡簡單列出!

從一個沒有約束的架構(NullStyle)開始,不斷的新增約束,使此架構進化為需要的架構

Null Style:元件之間沒有顯著邊界的系統,一個沒有約束的架構

Client-Server

  • 約束:分離關注點(客戶端介面和服務端資料儲存)
  • 優勢:客戶端和服務端可以獨立的進化。客戶端可以與多個服務端通訊,服務端可以方便的伸縮
  • 劣勢:降低了效能

Stateless

  • 約束:通訊必須在本質上是無狀態的。從客戶到伺服器的每個請求都必須包含理解該請求所必需的所有資訊,不能利用任何儲存在伺服器上的上下文,會話狀態因此要全部儲存在客戶端
  • 優勢:改善可見性,監視系統不必為了確定一個請求的全部性質而去檢視該請求之外的多個請求。改善可靠性,減輕了從區域性故障中恢復的任務量。改善可伸縮性,不必在多個請求之間儲存狀態,從而允許伺服器元件迅速釋放資源,並進一步簡化其實現,因為伺服器不必跨多個請求管理資源的使用。
  • 劣勢:降低網路效能,由於不能將狀態資料儲存在伺服器上的共享上下文中,因此增加了在一系列請求中傳送的重複資料(每次互動的開銷)。將應用狀態放在客戶端,降低了伺服器對於一致的應用行為的控制

Cache

  • 約束:一個請求的響應中的資料被隱式地或顯式地標記為可快取的或不可快取的
  • 優勢:改善網路效能。如果響應是可快取的,那麼客戶端快取就可以為以後的相同請求重用這個響應的資料。可部分或全部消除一些互動,從而通過減少一系列互動的平均延遲時間,來提高效率、可伸縮性和使用者可覺察的效能。
  • 劣勢:可能降低可靠性,快取中陳舊的資料與將請求直接傳送到伺服器得到的資料可能差別很大。

Uniform InterfaceREST核心特徵):

  • 約束:元件之間要有一個統一的介面,包括四個子約束:
    • 資源的識別(identification of resources):每個資源都有各自的識別符號。客戶端在請求時需要指定該識別符號。客戶端所獲取的是資源的表述,如HTML,XML 或 JSON 格式等。
    • 通過表述操作資源(manipulation of resources through representations):客戶端操作的是資源的表述,而不是資源本身。
    • 自描述的訊息(self-descriptive messages):每條訊息都包含足夠的資訊來描述如何處理該訊息。
    • 超媒體作為應用狀態引擎(HATEOAS)(hypermedia as the engine of application state):客戶端通過伺服器提供的超媒體內容來了解如何操作表述,通過將對錶述的操作提交到服務端,服務端來操作資源,繼而改變服務端的狀態。
  • 優勢:簡化整體架構,改善可見性,促進獨立的可進化性
  • 劣勢:降低了效率。資訊都使用標準化的形式來轉移,而不能使用特定於應用的需求的形式。

** Layered System**:

  • 約束:通過限制元件的行為,將架構分解為若干等級的層。
  • 優勢:通過將元件對系統的知識限制在單一層內,為整個系統的複雜性設定了邊界,並且提高了底層獨立性。使用層來封裝遺留的服務,使新的服務免受遺留客戶端的影響;通過將不常用的功能轉移到一個共享的中間元件中,從而簡化元件的實現。中間元件還能夠通過支援跨多個網路和處理器的負載均衡,來改善系統的可伸縮性
  • 劣勢:增加了資料處理的開銷和延遲,因此降低了使用者可覺察的效能。可以通過在中間層使用共享快取來彌補這一缺點。

Code-On-Demand(可選):

  • 約束:一個客戶端元件知道如何訪問一組資源,但不知道如何處理它們。它向一個遠端伺服器傳送對於如何處理資源的程式碼的請求,接收這些程式碼,然後在本地執行這些程式碼
  • 優勢:能夠為一個已部署的客戶端新增功能,改善了可擴充套件性和可配置性;當代碼能夠使它的動作適應於客戶端的環境,並在本地與使用者互動而不是通過遠端互動時,能夠得到更好的使用者可覺察效能和效率。由於伺服器將工作交給了客戶端(否則將消耗伺服器的資源),從而改善了伺服器的可伸縮性
  • 劣勢:由於需要管理求值環境,降低了簡單性,在一些情況下可以通過簡化靜態的客戶端功能得到補償。最大的限制是由於伺服器傳送程式碼而不是簡單的資料,因此缺乏可見性。如果客戶端無法信任伺服器,缺乏可見性會導致明顯的部署問題。

總結

可能看完了推導,你還是不知道REST是什麼!下面我通過一個列子來解釋什麼是「REST」!

舉個例子

我們先看看Fielding博士為什麼要設計REST?Fielding博士在論文裡提到了,他設計REST是為了指導現代Web架構的設計與開發!基於REST,Fielding博士設計了HTTP1.1!也就是說,HTTP1.1是符合REST的!所以要搞懂REST,只要理解HTTP1.1就可以了!

如果你做過Web應用,那麼CS,分層,無狀態,快取應該都很好理解,這裡就不贅述了!按需程式碼就是類似Flash,Applet這類Web端應用,用以擴充套件Web功能的!

這裡只說一下「統一介面」這個約束!

我們就以一個簡單的HTTP請求來解釋REST!

比如你輸入www.abc.com時:

  • 你通過標示符來定位到www.abc.com網站的首頁,abc站點將首頁資源組裝成Response資訊返回到你的瀏覽器(資源的識別)
  • 返回的內容頭裡(HTTP header),告訴了瀏覽器該如何處理返回的資訊(自描述的訊息)
  • 返回的資訊體(HTTP body),一般是HTML格式,它是你訪問的首頁資源的表述,裡面包含了你能對這個表述進行的操作,比如能訪問哪些連結,能提交哪些資料(超媒體作為應用狀態引擎)
  • 你點選連結後,這是對錶述的操作,你根本沒有接觸到真實的資源(通過表述操作資源)
  • 服務端接收到你的請求後,將對應連結的資源組裝成Response資訊返回(此時應用的狀態就改變了)。
  • 瀏覽器接到返回後,將頁面渲染出來,你可以進行下一步的操作。

什麼是RESTful

上面解釋了什麼是「REST」!現在來解釋一下什麼是RESTful!

前面說了,REST是一組架構約束!那麼,如果一個應用滿足了REST約束,那麼我們就可以稱這個應用是RESTful的

雖然,很多系統自稱是RESTful的,但是,實際上,絕大部分系統都不是RESTful的,或者不是完全RESTful的!Fielding博士對這個問題,發表了一篇博文,明確什麼系統才能稱為是符合我REST的!文中明確說明,系統必須滿足HATEOAS約束才能稱為是符合REST的!而HATEOAS很難實現!因為有人的參與!

為了緩解這個尷尬,Richardson 提出了「REST成熟度模型」。該模型把 REST 服務按照成熟度劃分成 4 個層次:

  • 第一個層次(Level 0)的 Web 服務只是使用 HTTP 作為傳輸方式,實際上只是遠端方法呼叫(RPC)的一種具體形式。SOAP 和 XML-RPC 都屬於此類。
  • 第二個層次(Level 1)的 Web 服務引入了資源的概念。每個資源有對應的識別符號和表述。
  • 第三個層次(Level 2)的 Web 服務使用不同的 HTTP 方法來進行不同的操作,並且使用 HTTP 狀態碼來表示不同的結果。如 HTTP GET 方法來獲取資源,HTTP DELETE 方法來刪除資源。
  • 第四個層次(Level 3)的 Web 服務使用 HATEOAS。在資源的表述中包含了連結資訊。客戶端可以根據連結來發現可以執行的動作。

從上述 REST 成熟度模型中可以看到,使用 HATEOAS 的 REST 服務是成熟度最高的,也是推薦的做法。

  • 對於不使用 HATEOAS 的 REST 服務,客戶端和伺服器的實現之間是緊密耦合的。客戶端需要根據伺服器提供的相關文件來了解所暴露的資源和對應的操作。當伺服器發生了變化時,如修改了資源的 URI,客戶端也需要進行相應的修改。
  • 而使用 HATEOAS 的 REST 服務中,客戶端可以通過伺服器提供的資源的表達來智慧地發現可以執行的操作。當伺服器發生了變化時,客戶端並不需要做出修改,因為資源的 URI 和其他資訊都是動態發現的。

現在大部分的自稱是RESTful的系統,一般只能達到第三個層次!

HATEOAS的難點

HATEOAS為什麼難以實現?是因為客戶端無法決策!HTTP能實現RESTful,是因為瀏覽器只是將表述以及對資源的操作選項展示了出來,至於具體該如何操作,是由使用瀏覽器的人來決定的!也就是說,雖然服務端告訴了客戶端操作的可選項,但是客戶端沒辦法知道該選擇什麼! 網頁瀏覽是有人的參與的,但是RESTful API是沒有人蔘與的,這就導致RESTful API的客戶端難以做出決定,該做什麼!

可行的解決辦法是:

  • 語義分析:客戶端具有語義分析能力,能夠自動的分析出後面需要執行哪個操作,目前這個很難實現
  • 客戶端領域設計:客戶端引入領域設計,在一個領域內,客戶端和服務端達成共識,在特定領域內,目前有哪些操作。不過這個還是做不到只修改服務端就可以實現系統的進化。服務端進化後,客戶端需要做對應的調整才可以完成整個系統的進化。

參考資料