讓 gRPC 提供 REST 服務

讓 gRPC 提供 REST 服務

Intro

gRPC 是一個高性能、開源和通用的 RPC 框架,面向移動和 HTTP/2 設計。

gRPC 基於 HTTP/2 標準設計,帶來諸如雙向流、流控、頭部壓縮、單 TCP 連接上的多復用請求等特。這些特性使得其在移動設備上表現更好,更省電和節省空間佔用。

gRPC 是一個很流行的現代化 RPC 框架,它以 HTTP/2 為通訊協議基礎,gRPC 默認使用 protocol buffers 作為介面定義語言,來描述服務介面和有效載荷消息結構。

儘管 gRPC 有很多應用,但是更為常用的還是基於 HTTP/1.1 的 REST 服務,應用更廣,那麼能否讓 gRPC 同時提供 REST 服務呢?答案是肯定的,現在有一個實驗性的項目(gRPC HTTP API )正在進行,如果覺得這個項目不錯,歡迎在 Github 上進行回饋,將你的意見回饋給 gRPC 團隊或者去點個贊以提升項目的優先順序 //github.com/grpc/grpc-dotnet/issues/167

gRPC loves REST

Sample

Proto

首先我們來看一下 proto file:

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 中註冊服務:

services.AddGrpcHttpApi();

如果引用了 swagger,也要註冊相應的服務:

services.AddSwaggerGen(c =>
    {
        c.SwaggerDoc("v1", new OpenApiInfo { Title = "My API", Version = "v1" });
    })
    .AddGrpcSwagger();

這樣就可以了

Client Sample

客戶端調用示例如下:

using var client = new HttpClient()
{
    DefaultRequestVersion = HttpVersion.Version20,
    DefaultVersionPolicy = HttpVersionPolicy.RequestVersionOrHigher,
};
await InvokeHelper.TryInvokeAsync(async () =>
{
    var responseText = await client.GetStringAsync("//localhost:5001/v1/greeter/test");
    Console.WriteLine($"Response from https endpoint: {responseText}");
});
await InvokeHelper.TryInvokeAsync(async () =>
{
    var responseText = await client.GetStringAsync("//localhost:5000/v1/greeter/test");
    Console.WriteLine($"Response from http endpoint: {responseText}");
});

//
await InvokeHelper.TryInvokeAsync(async () =>
{
    var responseText = await client.GetStringAsync("//localhost:5000/v1/todo");
    Console.WriteLine($"Response from todo endpoint: {responseText}");
});

客戶端輸出示例:

伺服器端輸出示例:

完整的測試程式碼可以在 Github 獲取 //github.com/WeihanLi/SamplesInPractice/tree/master/GrpcSample

Known Issues

JSON Serialization

現在的 JSON 序列化是基於Google.Protobuf,這個實現有兩個問題:

  • 它是執行緒阻塞的(非 async
  • 沒有做過性能優化

Http proto file

需要在最終用戶的源程式碼中添加 google / api / annotations.protogoogle / 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 上回饋哈 //github.com/grpc/grpc-dotnet/issues/167

References

Tags: