架構風格:你真的懂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的客戶端難以做出決定,該做什么!

可行的解決辦法是:

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

參考資料


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM