網絡協議 21 - RPC 協議(中)- 基於 JSON 的 RESTful 接口協議

    上一節咱們瞭解了基於 XML 的 SOAP 協議,SOAP 的 S 是啥意思來着?是 Simple,可是好像一點兒都不簡單啊!設計模式

傳輸協議問題

    對於 SOAP 來說,好比我建立一個訂單,用 POST,在 XML 裏面寫明動做是 CreateOrder;刪除一個訂單,仍是用 POST,在 XML 裏面寫明瞭動做是 DeleteOrder。其實建立訂單徹底可使用 POST 動做,而後在 XML 裏面放一個訂單的信息就能夠了,而刪除用 DELETE 動做,而後在 XML 裏面放一個訂單的 ID 就能夠了。緩存

    因而上面的那個 SOAP 就變成下面這個簡單的模樣。服務器

POST /purchaseOrder HTTP/1.1
Host: www.cnblog.com
Content-Type: application/xml; charset=utf-8
Content-Length: nnn

<?xml version="1.0"?>
 <order>
     <date>2018-07-01</date>
      <className> 板栗燜雞 </className>
       <price>58</price>
  </order>

    並且 XML 的格式也能夠改爲另一種簡單的文本化的對象表示格式 JSON。網絡

    常常寫 Web 應用的應該已經發現,這就是 RESTful 格式的 API 的樣子。架構

協議約定問題

    然而 RESTful 可不只僅是指 API,而是一種架構風格,全稱 Representational State Transfer,表述性狀態轉移,來自一篇重要的論文《架構風格與基於網絡的軟件架構設計》(Architectural Styles and the Design of Network-based Software Architectures)。併發

    這篇文章從深層次,更加抽象地論證了一個互聯網應用應該有的設計要點,而這些設計要點,成爲後來咱們能看到的全部高併發應用設計都必需要考慮的問題,再加上 REST API 比較簡單直接,因此後來幾乎成爲互聯網應用的標準接口。app

    所以,和 SOAP 不同,REST 不是一種嚴格規定的標準,它實際上是一種設計風格。若是按這種風格進行設計,RESTful 接口和 SOAP 接口都能作到,只不事後面的架構是 REST 倡導的,而 SOAP 相對比較關注前面的接口。負載均衡

    並且因爲可以經過 WSDL 生成客戶端的 Stub,於是 SOAP 經常被用於相似傳統的 RPC 方式,也即調用遠端和調用本地是同樣的。框架

    然而本地調用和遠程跨網絡調用畢竟不同,這裏的不同還不只僅是由於有網絡而致使的客戶端和服務端的分離,從而帶來的網絡性能問題。更重要的問題是,客戶端和服務端誰來維護狀態。所謂的狀態就是對某個數據當前處理到什麼程度了。函數

    這裏舉幾個例子,例如,我瀏覽到哪一個目錄了,我看到第幾頁了,我要買個東西,須要扣減一下庫存,這些都是狀態。本地調用其實沒有人糾結這個問題,由於數據都在本地,誰處理都同樣,並且一邊處理了,另外一邊立刻就能看到。

    當有了 RPC 以後,咱們原本指望對上層透明,就像上一節說的「遠在天邊,盡在眼前」。因而使用 RPC 的時候,對於狀態的問題也沒有太多的考慮。

    就像 NFS 同樣,客戶端會告訴服務端,我要進入哪一個目錄,服務端必需要爲某個客戶端維護一個狀態,就是當前這個客戶端瀏覽到哪一個目錄了。例如,客戶端輸入 cd hello,服務端要在某個地方記住,上次瀏覽到 /root/liuchao 了,於是客戶的此次輸入,應該給它顯示 /root/liuchao/hello 下面的文件列表。而若是有另外一個客戶端,一樣輸入 cd hello,服務端也在某個地方記住,上次瀏覽到 /var/lib,於是要給客戶顯示的是 /var/lib/hello。

    不光 NFS,若是瀏覽翻頁,咱們常常要實現函數 next(),在一個列表中取下一頁,可是這就須要服務端記住,客戶端 A 上次瀏覽到 20~30 頁了,那它調用 next(),應該顯示 30~40 頁,而客戶端 B 上次瀏覽到 100~110 頁了,調用 next() 應該顯示 110~120 頁。

    上面的例子都是在 RPC 場景下,由服務端來維護狀態,不少 SOAP 接口設計的時候,也經常按這種模式。這種模式原來沒有問題,是由於客戶端和服務端之間的比例沒有失衡。由於通常不會同時有太多的客戶端同時連上來,因此 NFS 還能把每一個客戶端的狀態都記住。

    公司內部使用的 ERP 系統,若是使用 SOAP 的方式實現,而且服務端爲每一個登陸的用戶維護瀏覽到報表那一頁的狀態,因爲一個公司內部的人也不會太多,把 ERP 放在一個強大的物理機上,也能記得過來。

    可是互聯網場景下,客戶端和服務端就完全失衡了。你能夠想象「雙十一」,多少人同時來購物,做爲服務端,它能記得過來嗎?固然不可能,只好多個服務端同時提供服務,你們分擔一下。可是這就存在一個問題,服務端怎麼把本身記住的客戶端狀態告訴另外一個服務端呢?或者說,你讓我給你分擔工做,你也要把工做的來龍去脈給我說清楚啊!

    那服務端索性就要想了,既然這麼多客戶端,那你們就分分工吧。服務端就只記錄資源的狀態,例如文件的狀態,報表的狀態,庫存的狀態,而客戶端本身維護本身的狀態。好比,你訪問到哪一個目錄了啊,報表的哪一頁了啊,等等。

    這樣對於 API 也有影響,也就是說,當客戶端維護了本身的狀態,就不能這樣調用服務端了。例如客戶端說,我想訪問當前目錄下的 hello 路徑。服務端說,我怎麼知道你的當前路徑。因此客戶端要先看看本身當前路徑是 /root/liuchao,而後告訴服務端說,我想訪問 /root/liuchao/hello 路徑。

    再好比,客戶端說我想訪問下一頁,服務端說,我怎麼知道你當前訪問到哪一頁了。因此客戶端要先看看本身訪問到了 100~110 頁,而後告訴服務器說,我想訪問 110~120 頁。

    這就是服務端的無狀態化。這樣服務端就能夠橫向擴展了,一百我的一塊兒服務,不用交接,每一個人都能處理。

    所謂的無狀態,實際上是服務端維護資源的狀態,客戶端維護會話的狀態。對於服務端來說,只有資源的狀態改變了,客戶端才調用 POST、PUT、DELETE 方法來找我;若是資源的狀態沒變,只是客戶端的狀態變了,就不用告訴我了,對於我來講都是統一的 GET。

    雖然這隻改進了 GET,可是已經帶來了很大的進步。由於對於互聯網應用,大多數是讀多寫少的。並且只要服務端的資源狀態不變,就給了咱們緩存的可能。例如能夠將狀態緩存到接入層,甚至緩存到 CDN 的邊緣節點,這都是資源狀態不變的好處。

    按照這種思路,對於 API 的設計,就慢慢變成了以資源爲核心,而非以過程爲核心。也就是說,客戶端只要告訴服務端你想讓資源狀態最終變成什麼樣就能夠了,而不用告訴我過程,不用告訴我動做。

    仍是文件目錄的例子。客戶端應該訪問哪一個絕對路徑,而非一個動做,我就要進入某個路徑。再如,庫存的調用,應該查看當前的庫存數目,而後減去購買的數量,獲得結果的庫存數。這個時候應該設置爲目標庫存數(可是當前庫存數要匹配),而非告知減去多少庫存。

    這種 API 的設計須要實現冪等,由於網絡不穩定,就會常常出錯,於是須要重試,可是一旦重試,就會存在冪等的問題,也就是同一個調用,屢次調用的結果應該同樣,不能一次支付調用,由於調用三次變成了支付三次。不能進入 cd a,作了三次,就變成了 cd a/a/a。也不能扣減庫存,調用了三次,就扣減三次庫存。

    固然按照這種設計模式,不管 RESTful API 仍是 SOAP API 均可以將架構實現成無狀態的,面向資源的、冪等的、橫向擴展的、可緩存的。

    可是 SOAP 的 XML 正文中,是能夠聽任何動做的。例如 XML 裏面能夠寫 < ADD >,< MINUS > 等。這就方便使用 SOAP 的人,將大量的動做放在 API 裏面。

    RESTful 沒這麼複雜,也沒給客戶提供這麼多的可能性,正文裏的 JSON 基本描述的就是資源的狀態,沒辦法描述動做,並且可以出發的動做只有 CRUD,也即 POST、GET、PUT、DELETE,也就是對於狀態的改變。

    因此,從接口角度,就讓你死了這條心。固然也有不少技巧的方法,在使用 RESTful API 的狀況下,依然提供基於動做的有狀態請求,這屬於反模式了。

服務發現問題

    對於 RESTful API 來說,咱們已經解決了傳輸協議的問題——基於 HTTP,協議約定問題——基於 JSON,最後要解決的是服務發現問題。

    有個著名的基於 RESTful API 的跨系統調用框架叫 Spring Cloud。在 Spring Cloud 中有一個組件叫 Eureka。傳說,阿基米德在洗澡時發現浮力原理,高興得來不及穿上褲子,跑到街上大喊:「Eureka(我找到了)!」因此 Eureka 是用來實現註冊中心的,負責維護註冊的服務列表。

    服務分服務提供方,它向 Eureka 作服務註冊、續約和下線等操做,註冊的主要數據包括服務名、機器 IP、端口號、域名等等。

    另一方是服務消費方,向 Eureka 獲取服務提供方的註冊信息。爲了實現負載均衡和容錯,服務提供方能夠註冊多個。

    當消費方要調用服務的時候,會從註冊中心讀出多個服務來,那怎麼調用呢?固然是 RESTful 方式了。

    Spring Cloud 提供一個 RestTemplate 工具,用於將請求對象轉換爲 JSON,併發起 Rest 調用,RestTemplate 的調用也是分 POST、PUT、GET、 DELETE 的,當結果返回的時候,根據返回的 JSON 解析成對象。

    經過這樣封裝,調用起來也很方便。

小結

  • SOAP 過於複雜,並且設計是面向動做的,於是每每由於架構問題致使併發量上不去;
  • RESTful 不只僅是一個 API,並且是一種架構模式,主要面向資源,提供無狀態服務,有利於橫向擴展應對高併發。
相關文章
相關標籤/搜索