1. 程式人生 > >讓 gRPC 提供 REST 服務

讓 gRPC 提供 REST 服務

# 讓 gRPC 提供 REST 服務 ## Intro > [gRPC](http://www.oschina.net/p/grpc-framework) 是一個高效能、開源和通用的 RPC 框架,面向移動和 HTTP/2 設計。 > > gRPC 基於 HTTP/2 標準設計,帶來諸如雙向流、流控、頭部壓縮、單 TCP 連線上的多複用請求等特。這些特性使得其在移動裝置上表現更好,更省電和節省空間佔用。 gRPC 是一個很流行的現代化 RPC 框架,它以 HTTP/2 為通訊協議基礎,gRPC 預設使用 [protocol buffers](https://developers.google.com/protocol-buffers/) 作為介面定義語言,來描述服務介面和有效載荷訊息結構。 儘管 gRPC 有很多應用,但是更為常用的還是基於 HTTP/1.1 的 REST 服務,應用更廣,那麼能否讓 gRPC 同時提供 REST 服務呢?答案是肯定的,現在有一個實驗性的專案(`gRPC HTTP API `)正在進行,如果覺得這個專案不錯,歡迎在 Github 上進行反饋,將你的意見反饋給 gRPC 團隊或者去點個贊以提升專案的優先順序 https://github.com/grpc/grpc-dotnet/issues/167 ![gRPC loves REST](https://img2020.cnblogs.com/blog/489462/202102/489462-20210219082607125-1444375467.png) ## Sample ### Proto 首先我們來看一下 proto file: ``` protobuf syntax = "proto3"; // import "google/api/annotations.proto"; package greet.v1; service Greeter { rpc SayHello (HelloRequest) returns (HelloReply) { option (google.api.http) = { get: "/v1/greeter/{name}" }; } rpc SayHelloFrom (HelloRequestFrom) returns (HelloReply) { option (google.api.http) = { post: "/v1/greeter" body: "*" }; } } message HelloRequest { string name = 1; } message HelloRequestFrom { string name = 1; string from = 2; } message HelloReply { string message = 1; } ``` 和之前相比的變化就是引入了 `google/api/annotations.proto`,然後在宣告方法的地方聲明瞭 http 請求的方式和路由 ### Project update 除了 proto file 變化之外,我們還需要引用 `Microsoft.AspNetCore.Grpc.HttpApi` 這個包,為了更好的和 swagger 整合,也可以引用 `Microsoft.AspNetCore.Grpc.Swagger` 這是一個 swagger 的擴充套件 在 `Startup` 中註冊服務: ``` csharp services.AddGrpcHttpApi(); ``` 如果引用了 swagger,也要註冊相應的服務: ``` csharp services.AddSwaggerGen(c => { c.SwaggerDoc("v1", new OpenApiInfo { Title = "My API", Version = "v1" }); }) .AddGrpcSwagger(); ``` 這樣就可以了 ### Client Sample 客戶端呼叫示例如下: ``` csharp using var client = new HttpClient() { DefaultRequestVersion = HttpVersion.Version20, DefaultVersionPolicy = HttpVersionPolicy.RequestVersionOrHigher, }; await InvokeHelper.TryInvokeAsync(async () => { var responseText = await client.GetStringAsync("https://localhost:5001/v1/greeter/test"); Console.WriteLine($"Response from https endpoint: {responseText}"); }); await InvokeHelper.TryInvokeAsync(async () => { var responseText = await client.GetStringAsync("http://localhost:5000/v1/greeter/test"); Console.WriteLine($"Response from http endpoint: {responseText}"); }); // await InvokeHelper.TryInvokeAsync(async () => { var responseText = await client.GetStringAsync("http://localhost:5000/v1/todo"); Console.WriteLine($"Response from todo endpoint: {responseText}"); }); ``` 客戶端輸出示例: ![](https://img2020.cnblogs.com/blog/489462/202102/489462-20210219082440431-1799390005.png) 伺服器端輸出示例: ![](https://img2020.cnblogs.com/blog/489462/202102/489462-20210219082453989-1005085877.png) 完整的測試程式碼可以在 Github 獲取 https://github.com/WeihanLi/SamplesInPractice/tree/master/GrpcSample ## Known Issues ### JSON Serialization 現在的 JSON 序列化是基於`Google.Protobuf`,這個實現有兩個問題: - 它是執行緒阻塞的(非 `async`) - 沒有做過效能優化 ### Http proto file 需要在終端使用者的原始碼中新增 `google / api / annotations.proto`和 `google / api / http.proto`,以便Protobuf編譯器可以將它們與使用者的proto檔案一起載入。 如果以某種方式使用者不必關心這些檔案,那將是更好的開發人員體驗。 ## More 這個專案使用下來感覺還是挺方便的,相當於在 `proto` 檔案中加了 http 請求相關的註解,就可以自動提供 REST 服務,這樣對於 gRPC 和 REST 服務的整合就很方便了 唯一讓我覺得有一些美中不足的地方就是 http 只支援 Http2,如果 http 協議要支援 http1.1 的話,http請求 必須要 `https`,如果是 http2 就可以比較好的支援 http,但是大部分的客戶端都是 httpClient 都是直接請求的,大多沒有設定過 Http Version,要手動設定 http2 才可以 如果覺得還不錯,記得去 GitHub 上反饋哈 https://github.com/grpc/grpc-dotnet/issues/167 ## References - https://github.com/grpc/grpc-dotnet/issues/167 - https://docs.microsoft.com/en-us/aspnet/core/grpc/httpapi?view=aspnetcore-5.0 - http://james.newtonking.com/archive/2020/03/31/introducing-grpc-http-api - https://github.com/aspnet/AspLabs/tree/master/src/GrpcHttpApi - https://grpchttpapi.azurewebsites.net/ - https://github.com/WeihanLi/SamplesInPractice/tree/master/GrpcSample