API 架構風格抉擇:SOAP、REST、GraphQL 和 RPC 的特性、優勢與局限
兩個獨立的應用程序需要一個中介來相互通信。因此,開發人員通常會構建橋梁——應用程序編程接口 (API) ——以允許一個系統訪問另一個系統的信息或功能。
為了快速、大規模地集成應用程序,API 使用協議和/或規范來實現,以定義通過網絡傳遞的消息的語義和語法。這些規范構成了 API 架構。
隨著時間的推移,各種不同的 API 架構風格應運而生。每種架構風格都有其獨特的數據交換標準化模式。選擇之多,引發了關于哪種架構風格最佳的無休止的爭論。
API 風格隨時間變化
如今,許多 API 使用者將 REST 稱為“寧靜的 REST ”,并為 GraphQL 歡呼雀躍。而十年前,情況恰恰相反,REST 最終取代了 SOAP。這些觀點的問題在于,他們片面地選擇了一種技術本身,而沒有考慮其實際屬性和特性如何與實際情況相匹配。
在本文中,我們將保持客觀,按照出現的順序討論四種主要的 API 風格,比較它們的優缺點,并重點介紹每種風格最適合的場景。
四種主要 API 樣式比較
一、遠程過程調用(RPC)
遠程過程調用 (RPC)是一種允許在不同上下文中遠程執行函數的規范。RPC 擴展了本地過程調用的概念,但將其置于 HTTP API 的上下文中。
最初的 XML-RPC 存在問題,因為確保 XML 負載的數據類型非常困難。因此,后來的 RPC API 開始使用更具體的JSON-RPC規范,該規范被認為是 SOAP 的更簡單替代方案。gRPC是 Google 于 2015 年開發的最新 RPC 版本。憑借對負載均衡、跟蹤、健康檢查和身份驗證的可插拔支持,gRPC 非常適合連接微服務。
RPC 的工作原理
客戶端調用遠程過程,將參數和附加信息序列化為消息,然后將消息發送到服務器。服務器收到消息后,會反序列化其內容,執行請求的操作,并將結果返回給客戶端。服務器存根和客戶端存根負責參數的序列化和反序列化。
遠程過程調用機制
RPC 優點
簡單直接的交互。RPC使用 GET 方式獲取信息,其余操作則使用 POST 方式。服務器和客戶端之間的交互機制歸結為調用端點并獲取響應。
易于添加功能。如果我們的 API 有新的需求,我們可以輕松添加另一個端點來執行此需求:1)編寫一個新函數并將其置于端點之后;2)現在客戶端可以訪問此端點并獲取滿足設定需求的信息。
高性能。輕量級負載在網絡上傳輸順暢,性能出色,這對于共享服務器和在工作站網絡上執行的并行計算至關重要。RPC 能夠優化網絡層,使其能夠高效地處理每天在不同服務之間發送大量消息的情況。
RPC 缺點
與底層系統緊密耦合。API的抽象級別會影響其可重用性。API 與底層系統的耦合度越高,其對其他系統的可重用性就越低。RPC 與底層系統的緊密耦合使得系統內部函數與外部 API 之間無法建立抽象層。這引發了安全問題,因為底層系統的實現細節很容易泄露到 API 中。RPC 的緊密耦合使得可擴展性要求和松散耦合的團隊難以實現。因此,客戶端要么擔心調用特定端點可能帶來的副作用,要么會因為不理解服務器函數的命名方式而嘗試弄清楚要調用哪個端點。
可發現性低。在 RPC 中,無法自檢 API 或發送請求,也無法根據請求了解要調用的函數。
函數爆炸。創建新函數太容易了。因此,我們不是編輯現有函數,而是創建新函數,結果卻得到了一大堆難以理解的重疊函數。
RPC 用例
RPC 模式大約在 80 年代開始使用,但這并不意味著它就過時了。像 Google、Facebook(Apache Thrift)和 Twitch(Twirp)這樣的大公司都在內部使用 RPC 高性能變體來執行極高性能、低開銷的消息傳遞。他們的大型微服務系統要求內部通信在短消息中清晰明了。
命令 API。RPC是向遠程系統發送命令的正確選擇。例如,Slack API 非常注重命令:加入頻道、離開頻道、發送消息。因此,Slack API 的設計者以類似 RPC 的風格對其進行了建模,使其精簡、緊湊且易于使用。
面向內部微服務的客戶專屬 API。由于單一提供商和消費者之間直接集成,我們不想像 REST API 那樣花費大量時間通過網絡傳輸大量元數據。gRPC 和 Twirp 憑借高消息速率和高性能,是微服務的有力選擇。gRPC 在底層使用 HTTP 2,能夠優化網絡層,并使其能夠高效地在不同服務之間每天發送大量消息。但是,如果您的目標并非高網絡性能,而是在發布高度差異化的微服務的團隊之間建立穩定的 API 連接,那么 REST 能夠滿足您的需求。
二、簡單對象訪問協議 (SOAP)
SOAP是一種 XML 格式、高度標準化的 Web 通信協議。SOAP 由微軟在 XML-RPC 一年后發布,它繼承了 XML-RPC 的諸多特性。REST 隨后出現,兩者最初并行使用,但很快 REST 就贏得了普及。
SOAP 的工作原理
XML 數據格式拖累了諸多繁瑣的流程,再加上龐大的消息結構,使得 SOAP 成為最冗長的 API 樣式。
SOAP 消息由以下部分組成:
- 每封郵件開頭和結尾的信封標簽,
- 包含請求或響應的主體
- 如果消息必須確定任何細節或額外要求,則需要標頭,以及
- 錯誤通知,告知在請求處理過程中可能發生的任何錯誤。
SOAP 消息示例。來源:IBM
SOAP API 邏輯以 Web 服務描述語言 (WSDL) 編寫。該 API 描述語言定義了端點并描述了所有可執行的流程。這使得不同的編程語言和 IDE 能夠快速建立通信。
SOAP 支持有狀態和無狀態消息傳遞。在有狀態場景下,服務器會存儲接收到的信息,這些信息可能非常龐大。但對于涉及多方和復雜事務的操作來說,這樣做是合理的。
SOAP 的優點
語言和平臺無關。內置的創建基于 Web 的服務功能允許 SOAP 處理通信,并使響應與語言和平臺無關。
綁定多種傳輸協議。SOAP在傳輸協議方面非常靈活,可以適應多種場景。
內置錯誤處理。SOAP API 規范允許返回帶有錯誤代碼及其解釋的“重試”XML 消息。
一系列安全擴展。SOAP與 WS-Security 協議集成,滿足企業級事務質量要求。它確保事務內部的隱私性和完整性,同時允許在消息級別進行加密。
圖片
SOAP 消息級安全性:標頭元素和加密正文中的身份驗證數據
SOAP 的缺點
如今,許多開發人員由于多種原因,對于必須集成 SOAP API 的想法感到不安。
僅限 XML。SOAP消息包含大量元數據,并且僅支持請求和響應的詳細 XML 結構。
重量級。由于 XML 文件很大,SOAP 服務需要大量帶寬。
狹義的專業知識。構建 SOAP API 服務器需要深入了解所有相關協議及其嚴格限制的規則。
消息更新繁瑣。需要額外添加或刪除消息屬性,僵化的 SOAP 模式會降低其采用速度。
SOAP 用例
目前,SOAP 架構最常用于企業內部或與其信任的合作伙伴之間的內部集成。
高度安全的數據傳輸。SOAP的嚴謹結構、安全性和授權功能使其成為在 API 和客戶端之間執行正式軟件合同的最佳選擇,同時又能遵守 API 提供商和 API 消費者之間的法律合同。這正是金融機構和其他企業用戶選擇 SOAP 的原因。
三、表述性狀態轉移 (REST)
REST是一種不言自明的 API 架構風格,由一組架構約束定義,旨在被眾多 API 消費者廣泛采用。
當今最常見的 API 樣式最初由 Roy Fielding 于 2000 年在其博士論文中描述。REST 使服務器端數據能夠以簡單格式(通常是 JSON 和 XML)表示。
REST 的工作原理
REST 的定義不像 SOAP 那樣嚴格。RESTful 架構應遵循以下六個架構約束:
- 統一接口:允許以統一的方式與給定的服務器進行交互,無論設備或應用程序類型如何
- 無狀態:處理請求所需的狀態包含在請求本身中,并且服務器不存儲與會話相關的任何內容
- 緩存
- 客戶端-服務器架構:允許任何一方獨立發展
- 應用程序的分層系統
- 服務器向客戶端提供可執行代碼的能力
事實上,有些服務只是在一定程度上符合 RESTful 風格。它們的核心是 RPC 風格,將大型服務分解為資源,并高效利用 HTTP 基礎設施。但關鍵在于使用超媒體(HATEOAS),即超文本作為應用程序狀態引擎的縮寫。簡單來說,這意味著 REST API 的每次響應都會提供鏈接到所有相關信息的元數據,這些信息與如何使用該 API 有關。這實現了客戶端和服務器的解耦。因此,API 提供者和 API 使用者都可以獨立發展,而不會妨礙彼此的通信。
Richardson 成熟度模型是實現真正完整且實用的 API 的目標
“HATEOAS 是 REST 的一個關鍵特性。它真正成就了 REST 的 REST 之美。由于大多數人不使用 HATEOAS,他們實際上使用的是 HTTP RPC。” Reddit上有人發表了這樣激進的觀點。事實上,HATEOAS 是 REST 最成熟的版本。然而,實現 HATEOAS 非常困難,因為它需要比目前常用和構建的 API 客戶端更先進、更智能的 API 客戶端。因此,即使是如今真正優秀的 REST API 也并非總能做到這一點。正因如此,HATEOAS 主要作為 RESTful API 設計長期發展的愿景。
當一個服務同時實現了 REST 和 RPC 的部分功能時,REST 和 RPC 之間可能確實存在一個灰色地帶。REST 基于資源或名詞,而不是基于動作或動詞。
以動詞為中心的 RPC 中的操作與以名詞為中心的 REST 中的操作相反
在 REST 中,操作是使用 HTTP 方法完成的,例如 GET、POST、PUT、DELETE、OPTIONS 以及 PATCH。
資料來源:托馬斯·戴維斯
REST 的優點
客戶端與服務器解耦。REST盡可能地將客戶端與服務器解耦,從而實現比 RPC 更好的抽象。具有抽象級別的系統能夠封裝其細節,從而更好地識別和維護其屬性。這使得 REST API 足夠靈活,能夠隨著時間的推移不斷發展,同時保持系統穩定。
可發現性。客戶端和服務器之間的通信描述了一切,因此無需外部文檔即可了解如何與 REST API 交互。
緩存友好。REST復用了大量 HTTP 工具,是唯一允許在 HTTP 級別緩存數據的樣式。相比之下,在任何其他 API 上實現緩存都需要配置額外的緩存模塊。
支持多種格式。能夠支持多種數據存儲和交換格式,是 REST 目前成為構建公共 API 的主流選擇的原因之一。
REST 的缺點
沒有單一的 REST 結構。構建 REST API 沒有絕對正確的方法。如何建模資源以及需要建模哪些資源將取決于具體場景。這使得 REST 在理論上簡單,但在實踐中卻困難重重。
大負載。REST返回大量豐富的元數據,以便客戶端僅從其響應中就能了解應用程序狀態的所有必要信息。對于帶寬容量巨大的大型網絡管道來說,這種繁瑣的操作并不是什么大問題。但情況并非總是如此。這正是 Facebook 在 2012 年提出 GraphQL 風格描述的關鍵驅動因素。
過度獲取和不足獲取問題。REST響應包含的數據過多或過少,通常需要發起另一個請求。
REST 用例
管理 API。這類 API 專注于管理系統中的對象,面向眾多用戶,是最常見的 API 類型。REST 有助于此類 API 擁有強大的可發現性、完善的文檔,并且非常適合 REST 對象模型。
簡單的資源驅動型應用。REST是一種連接不需要查詢靈活性的資源驅動型應用的有效方法。
四、GraphQL 僅查詢所需數據
它需要多次調用 REST API 才能返回所需的人員信息。因此,GraphQL 的發明就是為了改變現狀。
GraphQL是一種描述如何發出精確數據請求的語法。對于包含大量相互引用的復雜實體的應用程序數據模型而言,實現 GraphQL 是值得的。
如何從 GraphQL 端點僅檢索所需數據
如今,GraphQL 生態系統正在通過 Apollo、GraphiQL 和 GraphQL Explorer 等庫和強大的工具不斷擴展。
GraphQL 的工作原理
GraphQL 首先構建一個模式 (Schema),它描述了 GraphQL API 中可能執行的所有查詢以及它們返回的所有類型。模式構建非常困難,因為它需要模式定義語言 (SDL) 中的強類型支持。
在查詢之前掌握了模式后,客戶端可以驗證其查詢,確保服務器能夠響應。到達后端應用程序后,GraphQL 操作將根據整個模式進行解釋,并解析為前端應用程序的數據。向服務器發送一個大規模查詢后,API 將返回一個 JSON 響應,其數據結構與我們請求的數據完全一致。
GraphQL 中的查詢執行
除了 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 設計人員可以選擇最適合項目的設計風格。
由于緊密耦合,RPC 適用于內部微服務,但它不適用于強大的外部 API 或 API 服務。
SOAP 雖然麻煩,但其豐富的安全功能對于計費操作、預訂系統和支付來說仍然是不可替代的。
REST 擁有最高的抽象度和最佳的 API 建模。但它往往負載更重,而且更繁瑣——如果你在移動設備上工作,這將是一個缺點。
GraphQL 在數據獲取方面取得了很大的進步,但并不是每個人都有足夠的時間和精力去掌握它。
歸根結底,嘗試一些特定風格的小用例是有意義的,看看它是否適合你的用例并能解決你的問題。如果可以,請嘗試擴展它,看看它是否適用于更多用例。