1. 程式人生 > >基於場景選擇微服務的API正規化:REST、GraphQL、Webhooks和gRPC

基於場景選擇微服務的API正規化:REST、GraphQL、Webhooks和gRPC

看過了太多關於REST的熱愛和斷言,我們有時會忘記,這隻諸多選擇之一。REST對於相當大範疇的API來說是一個非常好的標準,但在一些需要API設計風格更細緻入微的場景,還有其他的標準可供選擇。

為了幫助API開發者瞭解使用哪種API設計風格以及在什麼情況下使用,我們把REST與其他三種選擇放在一起進行了一個說明,即:gRPC, GraphQL和Webhooks。我們會提供一些實際的實踐案例,來分析它們的優缺點,以強調是什麼核心特徵使每個選項在特定場景下成為一個很好的選擇。

一、REST概述

在諸多選擇中,REST可能是最廣為人知的,因為它在Web API中應用十分廣泛。2000年,Roy Fielding在其博士論文中首次提出REST的概念。他描繪了由Web服務約束所定義的體系結構基礎藍圖,在該體系結構中,應用無狀態設計理念和構建Web API的標準化方法。

白小白

Roy Thomas Fielding博士是HTTP和URI等Web架構標準的主要設計者,Apache HTTP 伺服器的主要開發者。關於其博士論文的全文中文版,可以在下面這個地址得到,感興趣可以看一哈。

https://dwz.cn/7fMFNeRr

REST本質上是無狀態的,其構建方式使任何與REST相容的Web服務都可以無狀態的方式與文字化的資源表述進行互動,互動的過程採用GET、POST、PUT和其他HTTP方法作為標準化定義。

小白

此處的文字化的資源表述與文字化的資源是有區別的。所有格式的資源都可以進行文字化的表述,這是REST的特徵。

REST的主要特性之一是它是超媒體使能的。事實上,超媒體和REST的關係是如此緊密,以至於Roy Fielding曾經宣告,如果API不支援超媒體,那麼從技術上講就不是RESTful。超媒體意味著在REST API中,客戶端和伺服器是鬆耦合的,這使客戶端和伺服器在資源操作方面獲得了極大的自由。也因此,快速迭代、伺服器進化、資源彈性等元素才得以實現。

小白

所謂超媒體,即超文字+多媒體,這裡借用了超文字的概念並在其基礎上有所延伸。1963年,德特•納爾遜(Ted Nelson)創造了術語“超文字”。1981年,德特在他的著作中使用術語“超文字”描述了這一想法:建立一個全球化的大文件,文件的各個部分分佈在不同的伺服器中。通過啟用稱為連結的超文字專案,例如研究論文裡的參考書目,就可以跳轉到引用的論文。

超媒體的含義其實本質上是指REST將一切網路資源進行唯一的URI定位,而不論是Word、圖片或者音視訊檔案,並提供統一的操作方式,這種抽象的過程簡化了對於資源的獲取和使用,從而更加適合鬆耦合的微服務架構。

我的理解,所謂的超,有二層含義,一是將資源抽象為URI表達的過程,無論任何種類和格式的資源,其最終的網路存在都是一種固定規則的URI表現;二是類似超文字的含義,超文字將所有資源連結在一起形成一個大文件,而超媒體意味著你可以對不同顆粒度的REST API進行連結來達成應用目標。

超媒體的概念在後文還將解決大量與GraphQL相關的問題。

REST支援的特性遠不止這些,還有分層架構,高效快取,高可伸縮性等,使得REST成為一個高度可發現和高度可塑的解決方案,以解決許多限定場景和問題,還有不容低估的標準化HTTP表達,為終端使用者提供了上下文,並使大多數互動標準化。總之,REST是現代微服務API領域非常高效、有效和強大的解決方案。

二、REST案例:PayPal

REST API的一個示例應用是PayPal REST API。PayPal強大的核心業務功能之一是為支付處理提供整合系統。因此,需要使用API簡化這一過程。資源必須易於識別,呼叫必須是可解讀的(無論有沒有上下文),最重要的是,必須支援各種媒體,以便有效地處理各種各樣的支付型別和方法。

小白

關於可解讀性。REST 介面的定義強調了自描述“Self-descriptive”性。也就是上文中所講到的呼叫可被解讀的需求。無論是否有上下文,意味著每一次REST請求都包含關於這個請求的全部資訊而不依賴其他的環境資訊。這也是無狀態的含義之一。下面這個圖來自 https://dwz.cn/wcAR42Pu可以很好的說明這種特點。

48d8ba9a171387005114ab3925419b29575ce9cf

可以看到,第二個URI是可解讀的,因為包含了全部所需要的資訊,即,“查詢第2頁的訂單內容”。而第一個URI是依賴於上下文的,也不可解讀,因為他的含義是“查詢下一頁的訂單內容”,需要提供“當前是第幾頁”這個上下文資訊才能夠進行相關查詢或者被理解。

為此,PayPal API的設計理念是易於理解和易於整合。以下這個示例摘自其官方文件,顯示了API的一次呼叫如何列出一系列活動:

curl -v -X GET https://api.sandbox.paypal.com/v1/activities/activities?start_time=2012-01-01T00:00:01.000Z&end_time=2014-10-01T23:59:59.999Z&page_size=10 \

-H "Content-Type: application/json" \

-H "Authorization: Bearer Access-Token"

在這裡,我們看到了有效的RESTful實現的印記。遵循標準的HTTP表達正規化的GET方法恰如其分的完成了其檢索資源的使命。在本例中,資源被明確定義為“activities”,並允許指定時區和頁碼的查詢需求。此外,返回值是一種特定的、已知的、支援超媒體的格式。以上是一個概要性的REST介紹,也用實際的示例說明了,輕量級、無狀態的系統正是將資源交付給客戶端的過程中所需要的。

三、gRPC概述

REST基本上可被認為是較現代的一種設計風格,而gRPC則是對歷史悠久的RPC(遠端過程呼叫)的一種新的傳承。RPC是一種在遠端伺服器上執行過程的方法,類似於在離您的工作站數英里的朋友的計算機上執行程式。RPC有其自身的優點和缺點,事實上,這些缺點(同時也是SOAP等系統固有的問題)正是REST開發和實現的關鍵。

gRPC和REST之間的一個關鍵區別是RPC定義其互動方式的協商機制。REST通過在HTTP請求中標準化的表達來定義互動,RPC的功能則是基於限定在客戶端-伺服器之間的特定協議而不是由架構本身來進行定義。RPC在很大程度上讓客戶端只需要執行 (同時也只在這方面負有責任),而將大部分處理和計算工作轉移給承載資源的遠端伺服器。

因此,RPC在物聯網裝置和其他需要定製化通訊協議的低功耗裝置的解決方案中非常流行。REST經常被認為對資源要求過高,而RPC甚至可以用於極低功耗情況。

gRPC是RPC概念的進一步發展,並增加了廣泛的特性。其中最重要的特性是ProtoBufs。ProtoBufs是語言中立和平臺無關的協議,用於對資料進行序列化,這意味著所有通訊可以高效地序列化以進行高效的交流。此外,通過Google的基於令牌的系統呼叫SSL/TLS協議,gRPC建立了非常有效和強大的身份驗證系統。最後,gRPC是開源的,這意味著系統可以被審計、迭代以及建立程式碼分支等等。

小白

gRPC使用ProtoBufs來定義服務。ProtoBufs全稱是Protocol buffers,是由Google開發的一種靈活的、高效的、自動化的用於對結構化資料進行序列化的協議,類似於XML,但與XML相比,Protocol buffers序列化後的碼流更小、速度更快、操作更簡單。

四、gRPC案例:

GoogleCloud,Bugsnag

gRPC很難直接演示,這很大程度上是因為,根據其官方文件的表述,gRPC通常用於“計算的最後一英里”。換句話說,gRPC通常是用來驅動和促進異構服務和API之間的通訊的終端系統。

儘管如此,文件也指出,由於其可移植性,gRPC可以在移動計算場景下使用,同時也是一箇中間處理系統,用於處理來自Google Cloud Bigtable Client API,Google Cloud PubSub API,以及Google Cloud Speech API的資料。這得益於gRPC提供的標準傳輸機制和相對靈活的資料負載,可以最好地應用於大流量、主動、高頻次通訊的場景。

gRPC另一個生產案例是Bugsnag。Bugsnag工程團隊發現,相比RESTful,gRPC的最初的設計過程更加流暢,當然,由於教程和最佳實踐的缺乏,“開發和測試gRPC的門檻相當高”。但總的來說,延遲改進和傳輸成本的降低使得使用gRPC的應用對於Bugsnag來說是一個巨大的成功。

小白

Bugsnag,應用程式實時檢測應用,是一個可以針對應用程式崩潰錯誤進行實時檢測追蹤的軟體測試利器工具;幫助查詢、追蹤手機應用和網頁應用程式中出現的錯誤問題。很顯然,作為雲服務,流量、頻次都是Bugsnag需要考慮的問題。因而也更適合採用gRPC的方案。

五、GraphQL概述

GraphQL對客戶端-伺服器關係的解決方案是獨一無二的,在某種程度上是對傳統的逆轉。使用GraphQL,客戶端將決定他們想要哪些資料,以何種格式以及如何取得這些資料。這是對伺服器向客戶端發號施令的經典模式的逆轉,同時,GraphQL提供了大量的擴充套件功能。GraphQL與REST以及RPC完全不同,REST是一種體系結構,而RPC則是由客戶端和伺服器定義的特定協議(並在很大程度上契約是由伺服器端的資源屬性定義的)。

GraphQL的一個巨大好處,是在預設情況下,它通常只發送最小請求,而REST通常傳送完整請求(即默認同時傳送它擁有的所有內容)。正因為如此,GraphQL在一些特定的用例中更加適用,在這些場景中,需要更明確的資料型別定義,並且傾向於使用較小的資料包來進行傳輸。

有人說,GraphQL的好處往往被誇大了。比如,GraphQL的“無版本”的概念,就來源於廢棄舊的欄位,同時用新的欄位替換,這其實也是REST API在演進時所考慮的問題。但GraphQL並非是“更好的REST”或者“REST的下一步”,而是“客戶端和資料之間的新型關係”的另一種選擇。

小白

關於REST API的演進,應當瞭解一下REST成熟度的概念。《RESTful Web Services》的合著者Leonard Richardson提出了一個REST成熟度模型,雖然圍繞這一模型,爭論很多,Martin Fowler、Rest之父Roy Fielding、《RESTful Web Services Cookbook》作者Subbu Allamaraju都有不同的見解。但對於全面的理解REST與GraphQL的特點,還是有幫助的(關於成熟度模型的具體含義,可參考Mryqu的文章 https://dwz.cn/JaWs9yIH)。

在這一成熟度模型中的第4級,使用超媒體作為應用狀態引擎(HATEOAS);多個URI,多個HTTP方法。在資源的表達中包含了連結資訊。客戶端可以根據連結來發現可以執行的動作。實際上解答了很多現有文章對於REST和GraphQL的誤解。

一般認為,REST的多端點特性需要進行API的組合以及多次HTTP請求才能完成GraphQL一次完成的查詢。也就意味著上文中所說的,當伺服器端的資源發生變更,REST必須引入多版本的概念來解決,而GraphQL則只需要在查詢條件上稍作修改即可。

事實上,由於HATEOAS的存在,REST可以通過在返回的資源中引入連結的概念,就可以完成類似GraphQL一樣的批量查詢,包括客戶端智慧的根據服務端資源的反饋來確定下一步應該如何動作。即大量文章所指出的GraphQL的客戶端API 可以不隨伺服器端的變化而變化的特徵,REST API在演進到了HATEOAS的階段時,也是支援的。

“對於不使用 HATEOAS 的 REST 服務,客戶端和伺服器的實現之間是緊密耦合的。客戶端需要根據伺服器提供的相關文件來了解所暴露的資源和對應的操作。當伺服器發生了變化時,如修改了資源的 URI,客戶端也需要進行相應的修改。而使用 HATEOAS 的 REST 服務中,客戶端可以通過伺服器提供的資源的表達來智慧地發現可以執行的操作。當伺服器發生了變化時,客戶端並不需要做出修改,因為資源的 URI 和其他資訊都是動態發現的。”(來自成富的文章 https://dwz.cn/X6VG4lCS)所以,超媒體這個概念對於REST是如此的重要,也響應了前文講到的“Roy Fielding曾經宣告,如果API不支援超媒體,那麼從技術上講就不是RESTful”這個論斷。

六、GraphQL案例:GitHub

使用GraphQL的一個示例是GitHub GraphQL API。雖然最初的RESTful API很強大,並且完成了所請求的操作,但是GitHub團隊逐漸發現,REST API的靈活性不夠。在談到這個問題時,Github團隊表述是,API的響應“同時傳送的資料太多,卻並不包括消費者需要的資料,”這是導致團隊採納GraphQL的最初動因。

白小白

關於這一點,油管有個講述REST提供冗餘資訊的小電影,只有1分鐘,形象生動。還是個程式媛妹子講的,2333。 https://dwz.cn/x0vGzn8F

因此,GitHub需要一種將其內容傳遞給請求者的新的API,這種API不需要進行多次獨立、複雜的呼叫,可以允許使用者自定義他們的請求,來說明他們到底需要什麼。最重要的是,這種新的API仍然能夠處理大量REST API已經有效處理的基本請求(相容已有的REST請求)。為此,Github增加了對GraphQL的支援,以提供上述這些關鍵功能。

七、Webhooks概述

GraphQL是擴充套件API的一種選擇,gRPC是對傳統方法的重新配置,Wehooks是一種完全不同的提供資源的方法,與上述的所有方法都不同。Webhook,簡單來說,就是在事件發生時觸發的HTTP POST請求。

這又是一種對客戶機-伺服器模式的逆轉,在傳統方法中,客戶端從伺服器請求資料,然後伺服器提供給客戶端資料(客戶端是在拉資料)。在Webhook正規化下,伺服器更新所需提供的資源,然後自動將其作為更新發送到客戶端(伺服器是在推資料),客戶端不是請求者,而是被動接收方。

這種控制關係的反轉可以用來促進許多原本需要在遠端伺服器上進行更復雜的請求和不斷的輪詢的通訊請求。通過簡單地接收資源而不是直接傳送請求,我們可以更新遠端程式碼庫,輕鬆地分配資源,甚至將其整合到現有系統中來根據API的需要來更新端點和相關資料。

八、Webhook示例:

Foursquare,SendGrid

WebHooks是一個相對簡單和有效的設計理念,因此,其實現同樣簡單和有效。Foursquare使用Webhook的方法本質上是建立一個流程,使用者在其中“檢入(checks in)”,就會觸發一個Webhook將更新的內容推送到其他系統和門戶。通過這種方式,使用者可以直接與他們正在訪問的位置互動,同時通過所享用的服務的相似性來建立客人之間的社交關係。

白小白

Foursquare是一家基於使用者地理位置資訊(LBS)的手機服務網站,並鼓勵手機使用者同他人分享自己當前所在地理位置等資訊。利用Foursquare服務,手機使用者可“檢入”某個地點,該地點可為全球任何城市的一家飯店、好友家庭居住地或一家商店等等。一旦使用者檢入,Foursquare將把使用者當前所在位置通知給該使用者的其他好友。使用者可以分享關於某地某項產品和服務的體驗。如果某位使用者在特定地點檢入的次數最多,他將獲得該地點虛擬“市長”的頭銜。根據頭銜可以享受商家的免費服務。聽起來有點像大眾點評。

當深入WebHooks的實現細節時,我們通常會看到更復雜的整合場景。例如,SendGrid使用Webhook傳送事件資料更新給訂閱客戶,向其告知對許多統計指標的變化。SendGrid甚至實現了一種複合的Webhook方法來解析電子郵件!

白小白

SendGrid是一個電子郵件服務平臺,可以幫助市場營銷人員跟蹤他們的電子郵件統計資料。如果需要實時獲取傳送郵件的狀態(如:傳送成功與否,對方有沒有收到,收到之後的處理-開啟,刪除,判定為垃圾郵件等),就需要用到SendGrid的WebHook功能來進行實時的資料通知。

九、REST、GraphQL、Webhooks

和RPC的場景比較

顯而易見,這些選項中沒有一個比其他選項真正“更好”,而只是某一種更適合於其獨特的互動場景。我們可以將這些場景歸納如下:

 ●   REST: 一種著重於進行資料傳輸的依賴超媒體的無狀態體系結構。REST可以將各種各樣的資源繫結在一起,這些資源可能以不同的格式被請求用於不同的目的。REST本質上關心無狀態的資源管理,因此也更適用於這種場景。需要快速迭代和標準化HTTP表達的系統更適合採用REST。
 ●   gRPC: 一種用於請求資料的靈活而輕量級的系統。gRPC更適用於系統需要對一定量的資料進行例行處理的情況下,發出資料請求的客戶端要麼是低功耗的,要麼是資源苛刻型的。物聯網就是一個很好的例子。
 ●   GraphQL: 一種使用者可以自定義所需資料和格式的方法。GraphQL來自Facebook,其血統很好地展示了它的應用場景,即,請求者需要特定格式的資料來進行特定的使用,在這些場景中,資料格式及其之間的關係至關重要,沒有任何其他解決方案擁有同等程度的資料的組合提供能力。
 ●   Webhooks: 資料更新自動完成,而不需要請求。如果API主要用於更新客戶端資料的場景下,最好使用Webhooks。儘管可能這些API還具有其他功能,甚至是RESTful功能,但Webhook的主要用途應該是更新客戶端,在資源新建或者更新時提供更新的、指定的資料。

在這些特定選項中進行選擇,實際上是將您的業務功能與適當的方法相匹配,並確保系統在給定引數範圍內及時響應。

十、結語

選擇一種設計方法也許是API開發的早期最重要的決定。這種設計方法不僅是對API的構建,也影響著終端使用者將如何與API所代表的資源進行互動。換句話說,這不僅僅是開發者層面的實現方法選擇,而是定義了你將如何與你的消費者建立關係。

最終選擇哪種解決方案取決於哪一種更適合你的特定場景。每種解決方案都有其非常具體的目的,因此,說一種解決方案比另一種好是不公平的。更準確的說法是,在執行某種核心功能方面,有些解決方案比其他解決方案更加擅長(類似許多RESTful解決方案試圖模擬RPC功能的嘗試就有待商榷)。

在程式碼庫既定的情況下,只有你才能確定哪種解決方案最好。因此,做好功課,並從一開始就選擇正確的方法,以獲得一些真正的收益。你的程式碼將更加簡潔,響應性更強,並且適合當前的情況。

白小白:Phil Sturgeon在apisyouwonthate.com釋出了一張API風格選擇決策樹,正適合作為本文的補充閱讀內容,遺憾的是少了Webhook的部分,好在這部分原本似乎也不是那麼主流,原文地址是:http://t.cn/E7PVRU3

當然,這一決策樹並沒有考量REST在第4級成熟度的HATEOAS階段的超媒體特性對於一些問題的解決。但還是有一定的參考意義。

6ccfa8330a18d26a8a6c8ec391bf77af89035493

我將本圖文字化表達如下,並附加了一些個人的理解與本文的結合:

00、開始。

01、客戶端的型別:移動端、網頁、分散式,轉向2;其他,轉向3;

(這裡的其他,應該就是指一些IOT/低功耗裝置的場景,正如前文所述,gRPC更適合的情況)

02、客戶端是否使用共有的流程:是,轉向4;不是,轉向5;

(客戶端使用共有的流程,意味著API可以更容易標準化因而更適合採用REST,而相反,則意味著定製化查詢的需求更普遍,從而更適合合適GraphQL)

03、是否可以立即進行協調一致、原子化的部署:是,轉向12;不是,轉向14;

(RPC依賴伺服器和客戶端的自定義協議,一旦API寫就,很難做出變更。即使變更也不是原子化的,因為涉及到服務端與客戶端的程式碼定製化做出修改)

04、選擇REST。

05、是否網路快取很重要:是,轉向4;不是,轉向6;

06、是否服務端定義的客戶端快取很重要:是,轉向4;不是,轉向7;

(REST可以在很多層級更容易的實現快取,包括閘道器、第三方託管以及客戶端快取,上述的選擇事實上都是關於是否快取更加重要。)

07、是否優先考慮減少響應的有效載荷:是,轉向8;不是,轉向4;

08、資源是否還有壓縮的可能:是,轉向4;不是,轉向9;

09、是否需要減少互動次數或者批量獲取:是,轉向10;不是,轉向4;

(上述選擇都是關乎REST的“重”互動方式,正如前文所述,每一次響應是返回全部的資料,這就不利於頻繁互動的場景,除非資源可壓縮,否則更適合GraphQL的場景)

10、型別系統是必須的還是可選的:必須,轉向11;可選,轉向4;

(GraphQL是強資料型別的實現方式)

11、選擇GraphQL。

12、更多遠端系統執行的事件或者操作服務端:遠端命令居多,轉向13;操作服務端,轉向2;

(RPC要求服務端執行大量的計算邏輯)

13、選擇gRPC。

14、是否跨越有邊界的上下文:是,轉向2;不是,轉向13;

(REST請求帶有自描述性,並且資源可以不依賴上下文關係而被理解和識別)

原文釋出時間為:2018-10-13

本文作者:Kristopher

本文來自雲棲社群合作伙伴“EAWorld”,瞭解相關資訊可以關注“EAWorld”。