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