API架構風格對比:SOAP vs REST vs GraphQL vs RPC
最近一段時間關於GraphQL的討論很多,一些項目中也相繼用到了這種風格,但使用是否合理,是否存在殺雞用牛刀這樣的問題,還有待商榷。
譯自:Comparing API Architectural Styles: SOAP vs REST vs GraphQL vs RPC
兩個不同的應用需要一個中間程序才能互通,開發者通常會使用應用程序接口(API)進行搭橋,使一個系統能夠訪問另一個系統的信息或功能。
為了在擴容時快速集成應用,實際的API會使用協議或規范來定義消息傳遞的語義和語法。這些規范構成了API架構。
過去幾年曾出現了幾種不同的API架構風格,每種風格都有其特定的標准數據交互模式。而對API架構的選擇引起了無休止的討論。

現在,很多API用戶放棄REST,並擁抱GraphQL。而在十年之前,對於REST來說則是相反的情況,在於SOAP的競爭中,REST大獲全勝。這種觀念的問題在於用於單方面去選擇一個技術,而沒有考慮實際價值以及以與特定場景的匹配度。
本文將會按照API風格出現的順序對它們進行討論,對比各自的優劣勢,並給出各自適合的場景。

Remote Procedure Call (RPC):喚醒另一個系統的功能
RPC是一個規范,它允許在一個不同的上下文中遠程執行功能。RPC將本地程序調用擴展到了HTTP API的上下文中(RPC的最上層大部分都是HTTP)。
一開始的XML-RPC問題比較多,它很難保證XML載體的數據類型。后來出現了一個基於JSON-RPC的RPC API,由於JSON的規范更加具體,因此被認為是SOAP的替代品。 gRPC是一個谷歌在2015年開發的全新RPC版本,插件化支持負載均衡、跟蹤、健康檢查以及身份認證等,gRPC非常適用於微服務間的通信。
RPC如何工作
客戶端喚醒遠端程序,序列化參數,並在消息中添加額外的信息,然后將消息發送給服務端。在接收到客戶端的消息后,服務端會反序列化消息中的內容,執行請求的操作,並將結果返回給客戶端。服務端存根(stub)和客戶端存根(stub)負責參數的序列化和反序列化。

RPC的優點
- 直接簡單的交互方式:RPC使用GET獲取信息,並使用POST處理其他功能。服務端和客戶端的交互歸結為對后端的調用,並獲取響應結果。
- 方便添加功能:如果我們對API有新的需求,可以通過簡單地添加新的后端來滿足該需求:1)編寫一個新的功能,然后發布;2)然后客戶端就可以通過這個后端來滿足需求。
- 高性能:輕量載體提升了網絡傳輸的性能,這對於共享服務器以及在網絡上進行並行計算的工作站來說非常重要。RCP可以優化網絡層,使其可以每天在不同的服務間發送大量消息。
RPC的缺點
-
與底層系統的強耦合:API的抽象程度與其可復用性相關。與底層系統的耦合越高,API的可復用性就越低。RPC與底層系統的強耦合使其無法在系統和外部API之間進行抽象,同時也增加了安全風險,很容易在API中泄露底層系統的實現細節。RPC的強耦合使其很難實現需求擴展和團隊解耦,客戶要么會擔心調用特定后端可能帶來的副作用(如安全問題),要么會因為無法理解服務端的功能命名規則而不知道調用哪個后端。
這里說的"與底層系統"的耦合,並不是說與內核等底層實現之間的耦合,而是與底層服務的耦合,如與日志服務,鑒權服務等耦合。
-
可發現性低:RPC無法對API進行自省或無法通過發送的RPC請求來理解其調用的功能。
應該是RPC並沒有像REST API那樣相對嚴格的調用規范,因此有些調用會比較難以理解
-
功能爆炸:由於很容易添加新的功能,因此相比編輯現有的功能,新增的功能可能會導致大量功能重疊,也很難去理解。
RPC使用場景
RPC模式始於80年代,但它一直沒有過時。像Google,Facebook (Apache Thrift)和Twitch(Twirp) 這樣的大型公司利用RPC的高性能特性來獲得高性能、低開銷的消息處理能力(規模龐大的微服務使用短消息進行通信,需要保證通信的暢通)。
- 命令式API:RPC非常適合向遠端系統發送命令。例如,Slack API就是重命令的接口:加入頻道、離開頻道、發送消息等。因此Slack API的設計者可以使用RPC風格的模型,使功能更簡單、緊湊,也更方便使用。
- 用於內部微服務客戶API:在整合單個供應商和用戶時,我們不希望(像REST API那樣)花費大量時間來傳輸元數據。憑借高消息速率和消息性能,gRPC和Twirp是微服務使用RPC的典范。gRPC背后使用的是HTTP 2,因此能夠優化網絡層,每天可以在不同的服務間傳送大量消息。但如果不關心高性能網絡,轉而期望團隊間能夠使用穩定的API來發布不同的微服務,那么可以選擇使用REST。
Simple Objects Access Protocol (SOAP): 讓數據作為服務
SOAP是一種XML格式的,高度標准化的web通信協議。在XML-RPC面世一年之后,Microsoft發布了SOAP,SOAP繼承了XML-RPC的很多特性。而后出現了REST,二者並駕齊驅,但很快REST就后來居上。
SOAP如何工作
XML數據格式多種多樣,加上大量消息結構,使得SOAP稱為一種最冗長的API樣式。
一個SOAP消息包含:
- 每個消息的開始和結束都要包含一個信封標簽
- 包含請求或響應的消息體
- 標頭(如果消息必須確定某些具體要求或額外要求)
- 請求過程中的錯誤信息

SOAP API的邏輯是用Web服務描述語言(WSDL)編寫的,該API描述語言定義了后端並描述了可執行的流程。它允許使用不同的編程語言和IDEs快速配置通信。
SOAP同時支持有狀態和無狀態消息。在有狀態場景中,服務端會保存接收到的信息,該過程可能比較繁重,但對於涉及多方和復雜交易的操作來說是合理的。
SOAP的優點
- 語言和平台無關:支持創建基於Web的服務內置功能使SOAP能夠處理獨立於語言和平台的通信,並作出響應。
- 適用於各種傳輸協議:SOAP支持大量傳輸協議,可以用於多種場景。
- 內置錯誤處理:SOAP API規范可以返回Retry XML消息(攜帶錯誤碼和錯誤解釋)
- 大量安全擴展:集成了WS-Security,SOAP符合企業級事務質量。它為事務提供了隱私和完整性,並可以在消息層面進行加密

SOAP的缺點
如今,由於多種原因,很多開發人員對必須集成SOAP API的想法感到不安。
- 僅支持XML:SOAP消息包含大量元數據,且請求和響應僅支持使用冗長的XML結構。
- 厚重:由於XML文件的大小,SOAP服務需要比較大的帶寬。
- 狹窄的專業知識:構建SOAP API需要深刻理解各種協議,以及嚴格的協議規則。
- 乏味的消息更新:在添加和移除消息屬性時需要額外的工作量,這導致SOAP的采用率下降。
SOAP的使用場景
目前,SOAP架構大部分用於內部集成企業或其他可信任的伙伴。
- 高度安全的數據傳輸:SOAP的剛性結構、安全和授權能力使其特別適用於在遵守API提供者和API使用者之間的契約的同時,在API和客戶端之間履行正式的軟件契約。這也是為什么金融機構和其他企業用戶選擇SOAP的原因。
Representational state transfer (REST): 將數據作為資源
REST是一個自解釋的、由一組架構約束定義的API架構風格,並被很多API使用者廣泛采用。
作為當今最通用的API風格,它最初出現在2000年的Roy Fielding 的博士論文中。REST使用簡單格式(通常是JSON和XML)來表達服務側的數據。
REST如何工作
REST沒有像SOAP那樣嚴格。RESTful架構應該遵循以下六個架構約束:
- 統一接口:為一個給定的服務(無論是設備還是應用類型)提供統一的接口。
- 無狀態:處理請求本身所包含的請求狀態,而服務器不會存儲與會話相關的任何內容
- 緩存
- 客戶端-服務端架構:允許兩端獨立演進
- 應用系統分層
- 服務端可以給客戶端提供可執行的代碼
實際上,某些服務僅在一定程度上是RESTful的,而核心使用了RPC風格,將大型服務分割成多個資源,並有效地利用HTTP基礎設施。但關鍵部分使用的是超媒體(又稱HATEOAS,Hypertext As The Engine of Application State),意味着對於每個響應,REST API提供了如何使用API的所有元數據信息。REST使用這種方式來解耦客戶端和服務端,這樣,API提供者和消費者就可以獨立演進,且不會妨礙它們的通信。

Richardson Maturity Model as a goalpost to achieving truly complete and useful APIs, Source: Kristopher Sandoval
"HATEOAS 是REST的關鍵特性,這也是REST之所以稱為REST的原因。但由於很多人並不使用HATEOAS,導致他們實際上用的是HTTP RPC",這是Reddit上的一些激進意見。確實,HATEOAS是最成熟的REST版本,但很難實現比通常使用和構建的API客戶端更加高級和智能的API客戶端。因此,即使是如今非常好的REST API也不能保證面面俱到。這也是為什么HATEOAS主要作為RESTful API設計的長期開發願景。
REST和RPC之間有一些灰色區域,特別是當一個服務具有一部分REST特性,一部分RPC特性時。REST基於資源,而不是基於動作或動詞。

在REST中,會用到像GET, POST, PUT, DELETE, OPTIONS, PATCH這樣的HTTP方法。

REST的優點
- 解耦客戶端和服務端:REST的抽象比RPC更好,可以更好地解耦客戶端和服務端。具有一定抽象的系統可以更好地封裝其細節並維持其屬性。這使得REST API足夠靈活,可以在保持系統穩定的同時,隨時間進行演化。
- 可發現性:客戶端和服務端的通信描述了所有細節,因此無需額外的文檔來理解如何使用REST API進行交互。
- 緩存友好:重用了大量HTTP工具,REST是唯一一種允許在HTTP層緩存數據的風格。相比之下,要在其他API風格中實現緩存,則要求配置額外的緩存模塊。
- 支持多種格式:支持多種格式的數據存儲和交互功能也是使REST成為當前流行的構建公共APIs的原因之一。
REST的缺點
- 沒有單一的REST結構:不存在正確地構建REST API的方式。如何對資源進行建模,以及對哪些資源建模取決於具體場景,這使得REST在理論上是簡單的,但實踐上是困難的。
- 載荷較大:REST會返回大量元數據,因此客戶端可以從響應的信息中了解到應用的狀態。對於具有大容量帶寬的大型網絡通道來說,這種交互方式沒有問題。但實際情況並不總是這樣,這也是Facebook在2012年推出GraphQL風格的主要驅動因素。
- 過度獲取和不足獲取問題:由於有時候會出現包含的數據過多或過少的情況,導致在接收到REST的響應之后,通常還會需要另一個請求。
REST的使用場景
- 管理API:專注於管理系統中的對象,並面向多個消費者是最常見的API風格。REST可以幫助這類APIs實現強大的發現能力,良好的文檔記錄,並符合對象模型。
- 簡單資源驅動的APPs:REST是一種非常有用的方法,可用於連接不需要靈活查詢的資源驅動型應用。
GraphQL:僅請求需要的數據
它需要多次調用REST API才能返回所需的內容。 因此,GraphQL被認為是一種改變API規則的風格。
GraphQL 的語法描述了如何發起精確的數據請求。GraphQL適合那些相互之間具有復雜實體引用關系的應用數據模型。

現在,GraphQL生態擴展了相關的庫,並出現了很多強大的工具,如Apollo, GraphiQL, and GraphQL Explorer。
GraphQL如何工作
一開始,GraphQL會創建一個schema(模式),它描述了在一個GraphQL API中的所有請求以及這些請求返回的所有types。構建模式會比較困難,它需要使用模式定義語言(DSL)進行強類型輸入。
由於在請求前已經構建好了模式,因此客戶端可以對請求進行校驗,確保服務器能夠進行響應。在到達后端應用后,會有一個GraphQL操作,負責使用前端應用的數據來解析整個模式。在給服務端發送包含大量查詢的請求之后,API會返回一個JSON響應,內容正對應請求的資源。

除RESTful CRUD操作外,GraphQL還有訂閱功能,允許接收服務端的實時通知。
GraphQL 的優點
-
類型化的模式:GraphQL 會提前發布它可以做的事情,這種方式提升了可發現性。通過將客戶端指向GraphQL API,我們可以知道哪些查詢是可用的。
-
非常適合類似圖形的數據:適合深度關聯的數據,不適合扁平數據。
-
沒有版本控制:最好的版本控制就是不對API進行版本控制。
REST提供了多種API版本,而GraphQL是一種單一的、演化的版本,可以持續訪問新的特性,方便服務端代碼的維護。
-
詳細的錯誤消息:與SOAP類似,GraphQL提供了詳細的錯誤信息,錯誤信息包括所有的解析器以及特定的查詢錯誤。
-
靈活的權限:GraphQL允許在暴露特定的功能的同時保留隱私信息。而REST架構不能部分展示數據(要么全部顯示,要么全部隱藏)。
GraphQL 的缺點
- 性能問題:GraphQL用復雜度換來功能上的提升。在一個請求中包含太多封裝的字段可能會導致系統過載。因此,即時對於復雜的查詢,REST仍然是一個比較好的選擇。
- 緩存復雜:GraphQL 沒有使用HTTP緩存語義,需要客戶自定義。
- 需要大量開發前培訓:由於沒有足夠的時間弄清楚GraphQL 的基本操作和SDL,很多項目決定沿用REST方式。
GraphQL的使用場景
- 手機端API:這種情況下,對網絡性能和單個消息載體的優化非常重要。因此GraphQL為移動設備提供了一種更有效的數據載體。
- 復雜系統和微服務:GraphQL能夠將復雜的系統集成隱藏在API背后。從不同的地方聚合數據,並將它們合並成一個全局模式。這對於擴展遺留基礎設施或第三方API尤為重要。
如何選擇API模式
每種API項目都有不同的要求,通常基於如下幾點進行選擇:
- 使用的編程語言
- 開發環境,以及
- 涉及的人力和財務資源等
在了解到每種API設計風格之后,API設計者就可以根據項目的需要選擇最合適的API模式。
由於強耦合特性,RPC通常用於內部微服務間的通信,不適用於外部API或API服務。
SOAP比較麻煩,但它本身豐富的安全特性仍然是交易操作、訂單系統和支付等場景的不二之選。
REST具有高度抽象以及最佳的API模型。但往往會增加線路和聊天的負擔--如果使用的是移動設備,這是不利的一面。
在獲取數據方面,GraphQL邁出了一大步,但並不是所有人都有足夠的時間和精力來處理這種模式。
歸根結底,最好在一些小場景下嘗試每種API風格,然后看是否滿足需求,是否能夠解決問題。如果可以,則可以嘗試擴展到更多的場景。