淺談 REST 軟體架構風格 (Part.I) - 從了解 REST 到設計 RESTful!


REST 起源與簡述

REST (全名 Representational State Transfer) 為 2000 年 Dr. Roy Fielding 提出的一篇博士論文 (Architectural Styles andthe Design of Network-based Software Architectures [1]),這篇文章可以說是 Web 領域中的聖經。而在我們常接觸的 HTTP 1.1 (RFC 2616 [2]) 規格書中,也可以看到 Dr. Roy Fielding 的名字出現。我必須強調 REST 並不是一種標準或協定,簡單地說 REST 是一種軟體架構風格 ,適合應用在複雜的網路服務環境中,然而 HTTP 也是符合 REST 的架構的一項實作。其實 REST 的設計概念已經運用在許多大型的網路系統中,近幾年 RESTful Web Service 更是引起開發者的討論與重視,而 WSDL 2.0 也宣布支援 REST Web Service 的描述方式。目前有很多著名的 Web Service 都是遵循 REST 的理念進行設計,像是 Amazon AWS, eBay, Facebook, Yahoo Web Service, Google Web Service 等等。相較於傳統的 XML-PRC 與 SOAP 協定,REST 在設計上更加簡單且直覺,而且 REST 在 Web Service 上的實作可以算是比較輕量級的設計模式。雖然 REST 距今已經超過十年的歷史,但許多人對 REST 卻是一知半解,這也是我寫這篇文章的原由(文中只是個人淺見,歡迎指教)。右圖為 REST Triangle,說明了 REST 在實作上使用的 Nouns, Verbs 與 Content types。

再談 REST

許多人第一次聽到 REST 這個名詞都會想到 Ruby on Rails, 當然我們不可否認 RoR 對 RESTful 的貢獻與推廣佔了很大的戲份。一般來說我們都稱這樣的設計架構為 REST,然而朝著這樣的理念所設計出來的系統可稱為 RESTful,表示系統在設計上遵循著 REST 的理念與原則。而 RESTful 這樣的軟體架構在過去幾年的驗證下,確實發現 REST 非常符合現今的網路架構與行為模式,尤其在高運算能力的雲端運算服務(Cloud Computing)與分散式系統中更能夠展現 REST 優異的一面。當然也有反派的聲音出現,有些人認為像是 REST 這樣簡易的設計模式,對於錯綜複雜的大型系統而言是不足的。當然我也不得不承認 REST 對於某些特殊服務需求,確實不容易藉由 RESTful 得到完全合理的設計,但是當優點多餘缺點時,我還是會選擇 REST 的軟體架構風格。

先分享一下以前看過的一篇文章『How I Explained REST to My Wife [3]』(作者是一位印度藉程式設計師),文中藉由生活的例子來解釋 REST,算是比較容易理解 REST 的一篇文章。那麼什麼是 REST 呢?表象化狀態轉變?照字面解釋我想應該很難理解,基本上 REST 解釋目前對網路操作的行為,其實就是一種表徵化的狀態轉變。我相信讀者一定聽不懂,是的,連我自己都講不清楚!好吧,我舉個例子,我們在瀏覽器上輸入 http://blog.toright.com/ 就可以連到這個 Blog (送出 Request),網頁伺服器回應 HTML 訊息 (回應 Response),瀏覽器解析 HTML 並且呈現在畫面上 (Render),你看到了畫面。OK,我們換個角度重新定義,瀏覽器告訴網頁伺服器:請給我(動詞, verbs)這個網址 http://blog.toright.com/(名詞, nouns)HTML 格式文件(表徵, content types),接著瀏覽器在呈現 HTML 的過程中可能會連上另一個網址讀取網頁中的圖片(圖片格式即是另一種表徵),或者使用者點選 RSS Link 獲得 XML 文件(XML又是另一種表徵),甚至使用 POST 傳送表單文字新增一篇回應(POST即為另一個動詞, verbs)。如此 REST 便說明了操作 Web 的基本行為是一種表徵化狀態轉變,我們藉由操作 (動詞) 不同的 URL (名詞) 達成不同的資料 (表徵) 呈現方式。然而 HTTP 最初衷的設計竟是如此簡單,而我們卻把它變複雜了(後面會介紹到)。引述 How I Explained REST to My Wife [4] 文中的一段話:

Ryan: Sadly, no. Instead, the large majority are busy writing layers of complex specifications for doing this stuff in a different way that isn’t nearly as useful or eloquent. Nouns aren’t universal and verbs aren’t polymorphic. We’re throwing out decades of real field usage and proven technique and starting over with something that looks a lot like other systems that have failed in the past. We’re using HTTP but only because it helps us talk to our network and security people less. We’re trading simplicity for flashy tools and wizards.

譯:很遺憾,大多數的人為了達到同樣的目標,忙著用不同的方法去設計出複雜且不易使用的規格。不但名詞 (Nouns) 不通用,且動詞 (Verbs) 也不多態。我們拋棄了過去幾十年的失敗經驗,再度重蹈覆轍。我們使用 HTTP 不僅僅是幫助我們使用網路,而我們卻忽略了 HTTP 初衷簡單化 (Simplicity) 的設計理念,得到的卻是華而不實而絢麗的工具。

Representational State Transfer

我來介紹一下 REST 吧!以英文形容詞來說,美麗 (beauty) 的事物可以稱為 Beautiful,同理可證:設計為 REST 的系統我們就稱為 RESTful。實際上 REST 提出的概念是很抽象的(可以說不是很好理解),雖然許多 Example 已經表達了實作的要領,但是許多人還是常常將 REST 與 HTTP 綁在一起,畢竟 HTTP 僅是 REST 的實現 (或典範),而不會是唯一。在 Dr. Roy Fielding 所提及的文獻中,並未對 REST 的實作與細節有太多的著墨,目的是為了想讓我們把重點放在 REST 的設計架構本質中。

1. REST Constraints - REST 條件/原則

REST 在概念上提出了幾項 Constraints (條件/原則),當我們的設計系統符合這些 Constraints 的時候,我們可稱這個系統為 RESTful。REST 提及的 Constraints 如下:

    • Client-Server - 使用主從式架構設計。
    • Stateless - Client與Server的溝通不需依賴狀態,每一個 Request 必須包含所有需要的資訊,而不需依賴其他 Request 的狀態。如此便能夠,目的是為了改善 Visibility (可視度!?不是很懂!?), Reliability (容易從錯誤中復原,維持正常的運作), Scalability (輕易地將 Request 交給其他 Server 進行處理) 能力。
    • Cacheable - 可實作快取,且快取機制可以在 Client 或 Server 中實作。
    • Uniform Interface - 在 Components (後面會介紹 REST Components) 之間使用一致性的操作介面,為了透過介面降低耦合並提高獨立性 (Independent)。其中 REST 對 Interface 定義了四種限制(目前我認為 Uniform Interface 就是 REST 的核心價值,有效的藉由抽象介面來隔離實作細節):
    • Identification of resources : 唯一的資源識別方法 Resource Identifiers (Nouns),在 HTTP 即是 URL
    • Manipulation of resources through representations : 透過特定的操作方法來操作資源 (verbs),在 HTTP 即是 GET, POST, PUT ,DELETE Mothods
    • Self-descriptive messages : 自我描述資訊,在 HTTP 即是 Content-Type
    • Hypermedia as the engine of application state : (我不了解這想表達什麼!?殘念.......)
    • Layered System - 可分層的系統架構,目的在於隱藏介面後的實作細節,使得系統更容易實現快取、加密等等機制。
    • Code-On-Demand (optional) - 可執行程式碼的設計,像是 JavaScript(非必要實作項目)

2. REST Architectural Elements - REST 架構元素

REST 定義了三種角色,分別是 Data Elements, Connectors Components

2.1. REST Data Elements

    • Resource - the intended conceptual target of a hypertext reference
    • Resource identifier - URL, URN
    • Representation - HTML document, JPEG image
    • Representation metadata - media type, last-modified time
    • Resource metadata - source link, alternates, vary
    • Control data - if-modified-since, cache-control

一般來說 Distributed Hypermedia Architect (分散式超媒體架構) 通常有以下三個實作方式,也有各自的優缺點:

      1. 傳送者將資料 Render 後傳送給接受者,如此的方式對於接收者來說實作最簡單。但是由於傳送者的 Render 功能限制,系統的 Scalability (可擴展性) 相當的差。(傳統的 Web MVC 實作常看見這樣的架構)
      2. 封裝資料與 Render Engine 然後一起傳送給接收者。如此能夠有效的封裝資料,缺點為有可能造成過高的資料傳送成本。
      3. 傳送原始資料 (RAW Data) 與 Metadata 给接收者,根據使用者可用的 Engine 自行 Render。優點為傳送資料的成本較低,缺點為對於資料的隱藏性較差,而雙方必須對 Data Type 有一定的共識。

REST 混合了這三種實作方法,但 REST 最主要的貢獻在於明確地抽離 Client 與 Server 的耦合性,透過一致性的介面進行溝通,大幅增加 Server 的 Scalability (可擴展性)。

Resources and Resource Identifiers (資源識別子) - Data Elements 主要的概念為 Resources,在 REST 的系統架構中所有的 Entity (實體) 使用全域唯一的 Resource Identifiers 進行識別與定義(全域的目的為盡可能讓這樣的識別資源方法在不同系統中能夠正確地指向唯一的實體),並且有效地將實體資源與 Resource Identifiers 進行 mapping。資源存取者透過 Connectors 操作資源,然而很重要的一件事,這樣資源的識別命名方式能夠直覺與合理是最好不過的。

Representations - 在 REST 的概念中 Components 之前是透過 Representations 來表示這個資源目前的狀態,Representations 就像我們所熟悉的 Content-Type。假設我們取得某個人在某個網站當前的照片,image/jpg 這樣的 Content-Type 就是一種 Representations; 當然我們也可以取得這個人在這個網站當前的個人基本資料,或許 Content-Type 會使用 application/xml (又是另一種Representations)。Client (User Agent) 在實作上會依據 Representations (就是Content-Type) 來正確地 Render 訊息,這樣的行為是不是很像我們在使用的瀏覽器呢?

2.2. REST Connectors

REST Connectors 包含了以下五種型態:

      • Client - libwww, libwww-perl
      • Server - libwww, Apache API, NSAPI
      • Cache - browser cache, Akamai cache network
      • Resolver - bind (DNS lookup library)
      • Tunnel - SOCKS, SSL after HTTP CONNECT

這些 Connectors 透過抽象的介面與 Components 進行溝通,在 REST 中所使用的連線都必須是 Stateless (無狀態)。Connectors 主要的形態為 Client 與 Server,由 Server 監聽 Client 所發出的 Request 並且回應 Response(連線機制與HTTP完全相符)。然而 Cache 的機制可以實作在 Client 或 Server 中(像是 Browser 與 Server 的 Cache 機制);Resolver 能夠在 Client 與 Server 的溝通上正確的進行解析處理;Tunnel 可以強制進行加密等等的工作。有趣的是這些功能都是獨立的,對於操作者來說都是隱藏且不可見的。

2.3. REST Components

REST Components 包含以下四種角色

      • User Agent - Netscape Navigator, Lynx, MOMspider
      • Origin Server - Apache httpd, Microsoft IIS
      • Gateway - Squid, CGI, Reverse Proxy
      • Proxy - CERN Proxy, Netscape Proxy, Gauntlet

User Agent 使用 Client Connector 對 Server 進行連線(User Agent 就像是瀏覽器),並且實作如何正確地 Render 來至於 Server 的 Response。Origin Server 透過 Server Connector 並且提供一個通用的 Interface 來接收 Request,透過 Interface 隱藏 Resource 的實作細節。就像我們使用 Browser 瀏覽網站的時候,不需要知道網站是用什麼樣的技術實現的。而 Gateway 與 Proxy 能夠在 Client 與 Server 溝通的過程中增加效率與安全性。

HTTP Examples (RESTful Web Service)

我相信看過上面的介紹對 REST 應該還是有看沒有懂,因此加入這個章節來說明實作面(如果 REST 是心法,那麼 HTTP 就是招式。),透過 HTTP 來解釋 REST 將更容易了解 REST 的設計理念與架構,若是要以 HTTP 為基礎來設計 RESTful 系統,簡單的說:『善用 HTTP 就對了』,因為 HTTP 本身就是 REST 的實作,何必大費周章、脫褲子放屁呢。

我打算將這個章節獨立介紹(不然太長了),請參考另一篇文章『淺談 REST 軟體架構風格 (Part.II) - 如何設計 RESTful Web Service?』。

Conclusion

還記得我第一次接觸 REST 的時候也是摸不著頭緒,在還沒有了解 REST 的時候搞不清楚這到底在搞什麼?更確切的說,我根本不知道這能幹嘛?這樣的架構到底有什麼好處?當我漸漸以為自己了解 REST 之後,也漸漸地將系統設計為 RESTful。慢慢地,思考過去的設計竟然是多麼的荒謬,竟是如此笨重、複雜且愚蠢。頓時我忽然豁然開朗,『簡單』才是正真優異的系統設計法則。

好幾年前剛接觸 REST,當下只覺得當時採用的 REST Library 挺方便的,其實那時候並沒有真正了解 REST,只是知道定義 URL 與使用 GET, PUT, POST, DELETE 來操作 Web Service。經過一段時間的摸索與實驗,當然也少不了對這樣的設計架構琢磨與奮鬥。幾年後,我以為我已經明白 REST 了,事實並不然。我曾經以為 HTTP + CRUD Method + URL 就是 REST 的全貌,其實上錯了。這些只是 REST 部分概念的實作與應用方式罷了(當然不可否認 HTTP Web Service 是目前 RESTful 最好的實作範例)。目前我認為真正的 REST 不僅僅是 HTTP + CRUD Method + URL,真正的價值是在於 REST 低耦合的設計理念,然而 REST 架構在其他的訊息交換系統中依然可以實作這樣的概念,希望未來有機會能夠對 REST 有更進一步的體會與認識。

『簡單』才是最好的設計!

如果你不知道你不知道,你會以為你知道。

Reference

SlideShare

Facebook 留言

廣告

樂樂童裝