gRPC 與 REST:最佳 API 協議入門
已發表: 2022-07-22在當今的技術環境中,大多數項目都需要使用 API。 API 在服務之間架起溝通的橋樑,這些服務可能代表一個單一的、複雜的系統,但也可能駐留在不同的機器上或使用多種不兼容的網絡或語言。
許多標準技術解決了分佈式系統的服務間通信需求,例如 REST、SOAP、GraphQL 或 gRPC。 雖然 REST 是一種受歡迎的方法,但 gRPC 是一個有價值的競爭者,它提供了高性能、類型化的合約和出色的工具。
REST 概述
具象狀態轉移 (REST) 是一種檢索或操作服務數據的方法。 REST API 通常建立在 HTTP 協議之上,使用 URI 來選擇資源並使用 HTTP 動詞(例如,GET、PUT、POST)來選擇所需的操作。 請求和響應正文包含特定於操作的數據,而它們的標頭提供元數據。 為了說明,讓我們看一個通過 REST API 檢索產品的簡化示例。
在這裡,我們請求一個 ID 為11
的產品資源,並指示 API 以 JSON 格式響應:
GET /products/11 HTTP/1.1 Accept: application/json
鑑於此請求,我們的響應(省略不相關的標頭)可能如下所示:
HTTP/1.1 200 OK Content-Type: application/json { id: 11, name: "Purple Bowtie", sku: "purbow", price: { amount: 100, currencyCode: "USD" } }
雖然 JSON 可能是人類可讀的,但在服務之間使用時並不是最佳的。 引用屬性名稱的重複性(即使是壓縮的)也會導致消息臃腫。 讓我們看看解決這個問題的替代方案。
gRPC 概述
gRPC 遠程過程調用 (gRPC) 是一種開源、基於合約的跨平台通信協議,它通過向外部客戶端公開一組功能來簡化和管理服務間通信。
gRPC 建立在 HTTP/2 之上,利用雙向流和內置傳輸層安全性 (TLS) 等功能。 gRPC 通過序列化的二進制有效負載實現更高效的通信。 它默認使用協議緩衝區作為結構化數據序列化的機制,類似於 REST 對 JSON 的使用。
然而,與 JSON 不同的是,協議緩衝區不僅僅是一種序列化格式。 它們包括其他三個主要部分:
- 在
.proto
文件中找到的合約定義語言(我們將遵循最新的協議緩衝區語言規範 proto3。) - 生成的訪問函數代碼
- 特定於語言的運行時庫
服務上可用的遠程功能(在.proto
文件中定義)列在協議緩衝區文件的服務節點內。 作為開發人員,我們可以使用協議緩衝區的豐富類型系統來定義這些函數及其參數。 該系統支持各種數字和日期類型、列表、字典和可空值來定義我們的輸入和輸出消息。
這些服務定義需要對服務器和客戶端都可用。 不幸的是,除了提供對.proto
文件本身的直接訪問之外,沒有共享這些定義的默認機制。
這個示例.proto
文件定義了一個函數來返回一個產品條目,給定一個 ID:
proto3 的嚴格類型和字段順序使得消息反序列化比解析 JSON 的負擔要小得多。
比較 REST 與 gRPC
回顧一下,比較 REST 與 gRPC 時最重要的一點是:
休息 | gRPC | |
---|---|---|
跨平台 | 是的 | 是的 |
消息格式 | 自定義但通常是 JSON 或 XML | 協議緩衝區 |
消息負載大小 | 中/大 | 小的 |
處理複雜性 | 更高(文本解析) | 較低(定義明確的二進制結構) |
瀏覽器支持 | 是(本機) | 是(通過 gRPC-Web) |
在預期不太嚴格的合同和頻繁添加有效負載的情況下,JSON 和 REST 非常適合。 當合約傾向於保持靜態且速度至關重要時,gRPC 通常會勝出。 在我從事的大多數項目中,gRPC 已被證明比 REST 更輕量級和更高性能。
gRPC 服務實現
讓我們構建一個精簡的項目來探索採用 gRPC 是多麼簡單。
創建 API 項目
首先,我們將在 Visual Studio 2022 社區版 (VS) 中創建一個 .NET 6 項目。 我們將選擇ASP.NET Core gRPC 服務模板並命名項目(我們將使用InventoryAPI
)和其中的第一個解決方案( Inventory
) 。
現在,讓我們選擇 . NET 6.0(長期支持)選項用於我們的框架:
定義我們的產品服務
現在我們已經創建了項目,VS 顯示了一個名為Greeter
的示例 gRPC 原型定義服務。 我們將重新調整Greeter
的核心文件以滿足我們的需求。
- 為了創建我們的合約,我們將用 Snippet 1 替換
greet.proto
的內容,重命名文件product.proto
。 - 為了創建我們的服務,我們將用 Snippet 2 替換
GreeterService.cs
文件的內容,將文件重命名為ProductCatalogService.cs
。
該服務現在返回一個硬編碼的產品。 為了使服務工作,我們只需要更改Program.cs
中的服務註冊以引用新的服務名稱。 在我們的例子中,我們將重命名app.MapGrpcService<GreeterService>();
到app.MapGrpcService<ProductCatalogService>();
使我們的新 API 可運行。
公平警告:不是您的標準協議測試
雖然我們可能很想嘗試它,但我們無法通過針對其端點的瀏覽器測試我們的 gRPC 服務。 如果我們嘗試這樣做,我們會收到一條錯誤消息,指示必須通過 gRPC 客戶端進行與 gRPC 端點的通信。
創建客戶端
為了測試我們的服務,讓我們使用 VS 的基本 Console App 模板並創建一個 gRPC 客戶端來調用 API。 我命名我的InventoryApp
。
為方便起見,讓我們引用一個相對文件路徑,我們將通過該路徑共享我們的合同。 我們將手動添加引用到.csproj
文件。 然後,我們將更新路徑並設置Client
模式。 注意:我建議您在使用相對引用之前熟悉並相信您的本地文件夾結構。
以下是.proto
引用,它們同時出現在服務和客戶端項目文件中:
服務項目文件 (複製到客戶端項目文件的代碼) | 客戶項目文件 (粘貼和編輯後) |
---|---|
|
|
現在,為了調用我們的服務,我們將替換Program.cs
的內容。 我們的代碼將實現許多目標:
- 創建一個表示服務端點位置的通道(端口可能會有所不同,因此請查閱
launchsettings.json
文件以獲取實際值)。 - 創建客戶端對象。
- 構造一個簡單的請求。
- 發送請求。
準備發射
要測試我們的代碼,在 VS 中,我們將右鍵單擊解決方案並選擇Set Startup Projects 。 在解決方案屬性頁對話框中,我們將:
- 選擇Multiple startup projects旁邊的單選按鈕,然後在 Action 下拉菜單中,將兩個項目(
InventoryAPI
和InventoryApp
)設置為Start 。 - 單擊確定。
現在我們可以通過單擊 VS 工具欄中的開始(或按F5鍵)來啟動解決方案。 將顯示兩個新的控制台窗口:一個告訴我們服務正在偵聽,另一個向我們顯示檢索到的產品的詳細信息。
gRPC 合約共享
現在讓我們使用另一種方法將 gRPC 客戶端連接到我們的服務定義。 客戶最容易訪問的合同共享解決方案是通過 URL 提供我們的定義。 其他選項要么非常脆弱(通過路徑共享文件),要么需要更多努力(通過本機包共享合同)。 通過 URL 共享(就像 SOAP 和 Swagger/OpenAPI 所做的那樣)是靈活的並且需要更少的代碼。
首先,將.proto
文件作為靜態內容提供。 我們將手動更新我們的代碼,因為構建操作的 UI 設置為“Protobuf Compiler”。 此更改指示編譯器複製.proto
文件,以便可以從 Web 地址提供它。 如果通過 VS UI 更改此設置,則構建將中斷。 那麼,我們的第一步是將 Snippet 4 添加到InventoryAPI.csproj
文件中:
接下來,我們將代碼插入到ProductCatalogService.cs
文件頂部的代碼片段 5 中,以設置一個端點以返回我們的.proto
文件:
現在,我們在app.Run()
之前添加 Snippet 6,也在ProductCatalogService.cs
文件中:
添加 Snippets 4-6 後, .proto
文件的內容應該在瀏覽器中可見。
一個新的測試客戶端
現在我們要創建一個新的控制台客戶端,我們將使用 VS 的依賴嚮導連接到我們現有的服務器。 問題是該嚮導不使用 HTTP/2。 因此,我們需要調整我們的服務器以通過 HTTP/1 進行通信並啟動服務器。 隨著我們的服務器現在可以使用它的.proto
文件,我們可以構建一個新的測試客戶端,通過 gRPC 嚮導連接到我們的服務器。
- 要更改我們的服務器以通過 HTTP/1 進行通信,我們將編輯我們的
appsettings.json
JSON 文件:- 調整
Protocol
字段(位於路徑Kestrel.EndpointDefaults.Protocols
)以讀取Https
。 - 保存文件。
- 調整
- 為了讓我們的新客戶端讀取此
proto
信息,服務器必須正在運行。 最初,我們從 VS 的 Set Startup Projects 對話框啟動了之前的客戶端和服務器。 調整服務器解決方案以僅啟動服務器項目,然後啟動解決方案。 (現在我們已經修改了 HTTP 版本,我們的舊客戶端無法再與服務器通信。) - 接下來,創建新的測試客戶端。 啟動另一個 VS 實例。 我們將重複創建 API 項目部分中詳述的步驟,但這一次,我們將選擇控制台應用程序模板。 我們將把我們的項目和解決方案命名為
InventoryAppConnected
。 - 創建客戶端機箱後,我們將連接到我們的 gRPC 服務器。 在 VS 解決方案資源管理器中展開新項目。
- 右鍵單擊Dependencies ,然後在上下文菜單中選擇Manage Connected Services 。
- 在 Connected Services 選項卡上,單擊Add a service reference並選擇gRPC 。
- 在 Add Service Reference 對話框中,選擇URL選項並輸入服務地址的
http
版本(記得從launchsettings.json
中獲取隨機生成的端口號) 。 - 單擊完成以添加可以輕鬆維護的服務引用。
隨意對照此示例的示例代碼檢查您的工作。 因為,在後台,VS 生成了我們在第一輪測試中使用的相同客戶端,所以我們可以重用之前服務中的Program.cs
文件的內容。
當我們更改合約時,我們需要修改我們的客戶端 gRPC 定義以匹配更新的.proto
定義。 為此,我們只需要訪問 VS 的 Connected Services 並刷新相關的服務條目。 現在,我們的 gRPC 項目已經完成,很容易讓我們的服務和客戶端保持同步。
您的下一個候選項目:gRPC
我們的 gRPC 實現提供了使用 gRPC 的好處的第一手資料。 REST 和 gRPC 各有自己的理想用例,具體取決於合約類型。 但是,當這兩個選項都適合時,我鼓勵您嘗試 gRPC — 它會讓您在 API 的未來處於領先地位。