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