何時使用 GraphQL、gRPC 和 REST?
構(gòu)建 API 是現(xiàn)代工程中開發(fā)人員的最重要任務之一。這些 API 允許不同的系統(tǒng)進行通信和數(shù)據(jù)交換。雖然 REST 多年來一直是實現(xiàn) API 的事實標準,但今天也有新興的標準,如 gRPC 和 GraphQL。
什么是 API?
“應用程序編程接口”(API)是各種軟件服務之間的通信渠道。傳輸請求和響應的應用程序分別稱為客戶端和服務器。API 是一個外部軟件組件,使程序功能可供其他程序使用。
在下面的蜜蜂 ?? 插圖中,花朵充當服務器,蜂巢充當客戶端,蜜蜂提供通信手段(REST API 請求)。
什么是 API?(圖片來源:Rapid API)
存在不同的 API 架構(gòu)或協(xié)議,如 REST、gRPC 和 GraphQL。
為什么 gRPC 很重要?
gRPC 是由 Google 開發(fā)的高性能、開源、通用遠程過程調(diào)用(RPC)框架。它使用 Protocol Buffers (protobuf) 作為其接口定義語言。協(xié)議緩沖區(qū)是一種非常有效和快速的網(wǎng)絡數(shù)據(jù)序列化方式。這項技術(shù)使用 HTTP 2.0 協(xié)議 實現(xiàn)了 RPC API;然而,服務器和 API 開發(fā)者都無法訪問 HTTP。它遵循 客戶端-響應通信模型,由于 gRPC 能夠接收來自多個客戶端的多個請求并同時處理它們,因此支持雙向通信和流媒體。然而,gRPC 在瀏覽器支持方面仍然 相當有限。
如前所述,gRPC 默認使用 Protocol Buffers 來序列化有效載荷數(shù)據(jù)。Protocol Buffers 是緊湊的二進制序列化格式,用于結(jié)構(gòu)化數(shù)據(jù)。它們使用在 .proto 文件中定義的模式,允許在不同語言之間進行高效的編碼和解碼。它們提供了比傳統(tǒng)的 JSON 或 XML 更小的尺寸和更快的處理速度等優(yōu)勢。
gRPC 的優(yōu)點:
- 速度:由于 HTTP/2,gRPC 比 HTTP/1.1 上的 REST 更快、更高效。
- 多語言支持:提供工具在多種語言中生成客戶端和服務器代碼。
- 流媒體: 支持雙向流媒體,允許更互動的實時通信。
- 截止時間/超時: 內(nèi)置支持確保請求不會掛起。它自動解決重試、網(wǎng)絡問題等。
- 生態(tài)系統(tǒng): 支持身份驗證、負載均衡等。
gRPC 的缺點:
- 復雜: 您必須了解 Protocol Buffers 和 gRPC API。還必須預先定義模式。
- 瀏覽器支持有限: 由于依賴 HTTP/2,原生瀏覽器支持有限。
- 工具: 雖然在增長,但 gRPC 工具的成熟度不如 REST。您可以使用 gRPCurl 或 Postman 來測試這些 API。
- 不可讀性: 由于它們是二進制的,它們難以調(diào)試,就像您可以使用基于文本的格式(如 XML 或 JSON)那樣。
gRPC
什么是 Protocol Buffers?
Protocol Buffers,通常縮寫為 Protobuf,是 Google 在 2008 年設計的一種序列化結(jié)構(gòu)化數(shù)據(jù)的方法。與 XML 或 JSON 類似,它們更精簡、更快,因為它們是二進制的,使它們成為與服務器通信或高效存儲數(shù)據(jù)的應用程序的首選。
使用 Protocol Buffers 進行數(shù)據(jù)序列化涉及幾個步驟:
- 定義數(shù)據(jù)結(jié)構(gòu):您從在 .proto 文件中定義數(shù)據(jù)結(jié)構(gòu)開始。這包括指定數(shù)據(jù)類型(整數(shù)、字符串、布爾值、自定義類型等)及其字段編號。
- 生成源代碼:使用 Protocol Buffers 編譯器(protoc)從文件中生成您所需語言的源代碼。編譯器可以為多種語言生成代碼,包括 Java、C++、Python 等。
- 生成可執(zhí)行包:可執(zhí)行包也是與 Protobuf 代碼生成的源文件一起生成和部署的。在運行時,消息以二進制格式序列化和壓縮。
- 反序列化: 當接收方收到序列化的數(shù)據(jù)流時,他們可以使用生成的類輕松地將其轉(zhuǎn)換回結(jié)構(gòu)化格式。
什么是 REST API?
REST(Representational State Transfer,表述性狀態(tài)傳遞)不是一個框架或庫,而是一種用于構(gòu)建 Web 服務和 API 的架構(gòu)風格。在 REST 中,一切都是 由唯一 URL 標識的資源,這些資源使用 HTTP 方法(如 GET(檢索資源)、POST(創(chuàng)建新資源)、PUT 或 PATCH(更新資源)和 DELETE(移除資源))進行操作。
客戶端到服務器的每個請求都必須包含理解和完成請求所需的所有信息。服務器不存儲請求之間的客戶端上下文,簡化了設計并提高了可伸縮性。客戶端和服務器的 HTTP 請求和響應體攜帶 JSON 或 XML 表示 資源的狀態(tài)。
在他的 博士論文中,計算機科學家 Roy Fielding 在 2000 年介紹并定義了表述性狀態(tài)傳遞。 REST API 的好處:
- 簡單性: 使用標準 HTTP 方法和常見數(shù)據(jù)格式使 REST API 易于理解和實現(xiàn)。
- 互操作性: REST API 促進了互操作性,因為不同的應用程序可以無縫交互,無論使用的是哪種編程語言或平臺。
- 可伸縮性: REST API 的無狀態(tài)性質(zhì)允許輕松擴展以處理大量請求。
- 靈活性: 由于其多功能設計原則,REST API 可以適應各種用例。
REST API 的缺點:
- 無狀態(tài)性: REST 依賴于無狀態(tài)事務,這意味著每個請求都必須獨立完成所有信息。對于需要在多個請求之間維護狀態(tài)的工作流程(如電子商務網(wǎng)站上的購物車)來說,這可能很麻煩。
- 有效載荷大小有限: REST 中的數(shù)據(jù)傳輸通常通過 JSON 或 XML 有效載荷進行,如果您處理復雜數(shù)據(jù)或多個查詢,這可能會變得相當大。這可能導致性能問題。
- 可發(fā)現(xiàn)性不足: REST API 本身并不容易讓用戶理解其功能或如何與之交互,這可能會為新用戶增加復雜性。
- 復雜查詢的性能: 對于從較大資源中檢索特定數(shù)據(jù)點,REST 可能不是理想選擇。其他選項,如 GraphQL,在這種情況下可能更有效。
REST 定義了一個 API 應該遵循的 六個架構(gòu)約束,以被認為是真正的 RESTful:
(1) 客戶端-服務器: 這種關(guān)注點的分離將客戶端(使用 API 的應用程序)與服務器(提供 API 的應用程序)分開。客戶端發(fā)起請求,服務器處理并發(fā)送響應。
(2) 無狀態(tài): 客戶端到服務器的每個請求都必須包含理解請求所需的所有信息。服務器不存儲有關(guān)客戶端的任何上下文。這簡化了通信并提高了可伸縮性。
(3) 統(tǒng)一接口: 此約束定義了一組規(guī)則,規(guī)定客戶端如何與服務器交互。這些規(guī)則包括:
- 基于資源: API 暴露客戶端可以交互的資源。URL 標識資源。
- 標準方法: 客戶端使用標準 HTTP 方法(GET、POST、PUT、DELETE)對資源進行操作。
- 表示: 客戶端和服務器之間的數(shù)據(jù)以 JSON 或 XML 等標準格式交換。
(4) 可緩存: 客戶端可以將服務器響應標記為可緩存。這允許客戶端本地存儲頻繁訪問的數(shù)據(jù),減輕服務器負載并提高性能。
(5) 分層系統(tǒng): 架構(gòu)可能包括客戶端和服務器之間的多個層次(代理、緩存、負載均衡器)。這些層可以提高性能、安全性和可伸縮性。
(6) 按需代碼(可選): 雖然不是嚴格必需的,但 RESTful API 可以選擇性地將可執(zhí)行代碼傳輸給客戶端。客戶端可以使用此代碼擴展其功能或本地處理數(shù)據(jù)。
REST 架構(gòu)約束
REST API 調(diào)用的一個例子 是當我們想要獲取 ID 為 500 的用戶信息時,使用 curl 命令行工具為 https://api.example.com 地址上的 API 發(fā)出的請求:curl -X GET https://api.example.com/users/500 -H "Accept: application/json"。最后一部分(Accept: application/json)是一個頭部,表明客戶端期望以 JSON 格式接收數(shù)據(jù)。響應將是 JSON 格式的結(jié)果,200 將是響應狀態(tài)碼。
REST API 架構(gòu)
即使在性能至關(guān)重要時 REST 不是最佳選擇,我們?nèi)匀豢梢栽谶@里做一些事情,如 緩存、分頁、有效載荷壓縮 等。
什么是 GraphQL?
GraphQL 是一種用于 API 的查詢語言,由 Meta 在 2015 年發(fā)布并開源。現(xiàn)在由 GraphQL 基金會 監(jiān)督。GraphQL 是一個服務器端運行時環(huán)境,使客戶端能夠從 API 請求所需的數(shù)據(jù)。與傳統(tǒng)的 REST API 不同,后者通常需要多個請求來獲取不同的數(shù)據(jù)片段,GraphQL 允許您在單個請求中指定所需的所有數(shù)據(jù)。GraphQL 規(guī)范在 2015 年開源。
因為 GraphQL 在向 API 發(fā)送查詢時不會過度或不足地獲取結(jié)果,它保證了使用 GraphQL 構(gòu)建的應用程序是 可伸縮、快速和穩(wěn)定 的。它還允許將多個操作組合成單個 HTTP 請求。
GraphQL API 按照 類型和字段 組織,而不是端點。使用 GraphQL Schema Definition Language (SDL),您將數(shù)據(jù)定義為模式。這個模式作為客戶端和服務器之間的契約,詳細說明了可以進行哪些查詢、可以獲取哪些類型的數(shù)據(jù)以及響應將是什么樣子。
GraphQL 的好處:
- 高效的數(shù)據(jù)獲取: 您只請求確切需要的數(shù)據(jù),消除了 REST 可能發(fā)生的過度獲取或不足獲取的問題。這可以顯著提高性能,尤其是對于復雜的數(shù)據(jù)模型。
- 靈活和聲明性: GraphQL 使用定義可用數(shù)據(jù)及其訪問方式的模式。這個模式允許開發(fā)人員編寫清晰、簡潔的查詢,指定他們確切的數(shù)據(jù)需求。
- 單個請求用于多個資源: 與 REST 不同,后者需要多個 API 調(diào)用來從不同端點獲取數(shù)據(jù),GraphQL 允許將查詢組合成單個請求以提高效率。
- 版本控制和向后兼容性: 可以通過版本控制實現(xiàn) GraphQL 模式更改,確保現(xiàn)有客戶端不受影響,同時允許未來增長。
GraphQL 的缺點:
- 查詢結(jié)構(gòu)的復雜性: 雖然靈活性是一個優(yōu)勢,但編寫復雜的 GraphQL 查詢可能具有挑戰(zhàn)性,并且需要仔細規(guī)劃以提高可讀性和可維護性。
- 緩存: 與利用內(nèi)置 HTTP 緩存機制的 REST API 相比,GraphQL 的數(shù)據(jù)緩存通常更復雜。
- 安全性: GraphQL 暴露了您的整個數(shù)據(jù)模式,因此必須采取適當?shù)陌踩胧┮苑乐刮唇?jīng)授權(quán)的訪問敏感數(shù)據(jù)。
- 學習曲線: 對于不熟悉 GraphQL 的開發(fā)人員,理解模式和查詢語法涉及學習曲線。
- 錯誤處理: 如果庫不解析響應體中狀態(tài)為 200 的錯誤,則客戶端必須使用更復雜的邏輯來處理它們。
它是如何工作的:
- 客戶端使用GraphQL語法定義查詢,精確指定數(shù)據(jù)應如何結(jié)構(gòu)化以及需要哪些字段。
- GraphQL服務器使用預定義的模式來確定可用的數(shù)據(jù)及其與其他數(shù)據(jù)的關(guān)系。這個模式定義了類型、字段以及類型之間的關(guān)系。
- 服務器根據(jù)模式執(zhí)行查詢。對于查詢中的每個字段,服務器都有一個相應的解析函數(shù)來獲取該字段的數(shù)據(jù)。
- 服務器返回一個JSON對象,其結(jié)構(gòu)直接反映了查詢內(nèi)容,填充了請求的數(shù)據(jù)。
GraphQL支持三種核心操作,定義了客戶端如何與服務器交互:
- 查詢(Queries): 用于從服務器檢索數(shù)據(jù)。這是GraphQL中最常用的操作。
- 變更(Mutations): 在服務器上修改數(shù)據(jù)。這可能涉及創(chuàng)建新數(shù)據(jù)、更新現(xiàn)有數(shù)據(jù)或刪除數(shù)據(jù)。
- 訂閱(Subscriptions): 用于在客戶端和服務器之間建立實時通信。服務器可以在請求的數(shù)據(jù)發(fā)生變化時更新客戶端。
GraphQL請求的一個示例包括一個操作和你請求或操作的數(shù)據(jù),例如:
這個查詢檢索ID為1的用戶的數(shù)據(jù)。它還獲取了該用戶帖子的嵌套數(shù)據(jù),包括它們的ID和標題。
響應是一個JSON對象,包含查詢或變更請求的實際數(shù)據(jù)和可選錯誤。
要了解更多關(guān)于GraphQL的信息,可以查看Eve Porcello和Alex Banks的書籍“Learning GraphQL”。
何時應使用GraphQL、gRPC和REST?
開發(fā)者在設計應用程序時可以選擇多種客戶端-服務器通信協(xié)議。在當代項目中,使用GraphQL、gRPC和REST相對常見。每種協(xié)議根據(jù)您的應用程序需求可以提供不同的優(yōu)勢。
- GraphQL 是一種靈活的數(shù)據(jù)請求方法,專注于特定請求并僅提供所需數(shù)據(jù)。它的客戶端驅(qū)動特性使其與其他API區(qū)別開來。客戶端做出所有決策,而不是處理它們。GraphQL的優(yōu)勢在于它與語言無關(guān),請求通過單一端點進行,并且是強類型的,因為它有模式。
- REST 是最受歡迎的一個。當一個領(lǐng)域可以被描述為一組資源時,它是一個很好的選擇。REST是一種用于數(shù)據(jù)傳輸?shù)臒o狀態(tài)架構(gòu)。它的優(yōu)勢包括是一個成熟的標準、簡單易用和良好的緩存支持。
- gRPC 是一個輕量級且快速的數(shù)據(jù)獲取系統(tǒng)。這里的主要區(qū)別是它如何描述其合同談判。它依賴于合同;架構(gòu)不是管理談判的內(nèi)容;它是服務器和客戶端之間的關(guān)系。雖然處理和計算被委托給遠程服務器,但大部分能力用于客戶端。它的主要優(yōu)勢是它有輕量級客戶端,高效率,使用協(xié)議緩沖區(qū)來發(fā)送/接收數(shù)據(jù),而且是開源的。
下圖顯示了最常見的API架構(gòu)風格的時間線。
API架構(gòu)風格時間線
因此,何時選擇這些協(xié)議中的每一個:
- 如果您正在構(gòu)建一個CRUD風格的Web應用程序或您處理結(jié)構(gòu)化數(shù)據(jù)良好,使用REST。它是公共API和需要被廣泛客戶端消費的服務的首選。
- 如果您的API是私有的并且關(guān)于行動或性能至關(guān)重要,使用gRPC。服務器到服務器通信的低延遲至關(guān)重要。它使用HTTP/2和ProtoBuf優(yōu)化了效率和速度。
- 如果您有一個公共API,需要在定制請求方面靈活,并希望將不同來源的數(shù)據(jù)添加到公共API中,請使用GraphQL。在客戶端-服務器通信中使用它,我們必須在單次往返中獲取所有數(shù)據(jù)。
gRPC與REST與GraphQL比較