作者: James Newton-King

gRPC 是一种高性能远程过程调用 (RPC) 框架。 gRPC 使用 HTTP/2、流式传输、Protobuf 和消息协定来创建高性能的实时服务。

gRPC 有一个限制,即不是所有平台都可以使用它。 浏览器并不完全支持 HTTP/2,这使得 REST API 和 JSON 成为将数据引入浏览器应用的主要方式。 尽管 gRPC 带来了很多好处,REST API 和 JSON 在新式应用中仍发挥着重要作用。 构建 gRPC 和 JSON Web API 给应用开发增加了不必要的开销。

本文讨论如何使用 gRPC 服务创建 JSON Web API。

gRPC JSON 转码是为 gRPC 服务创建 RESTful JSON API 的 ASP.NET Core 的扩展。 配置转码后,应用可以使用熟悉的 HTTP 概念调用 gRPC 服务:

  • HTTP 谓词
  • URL 参数绑定
  • JSON 请求/响应
  • gRPC 仍然可以用来调用服务。

    gRPC JSON 转码取代了 gRPC HTTP API ,这是一个替代的实验性扩展。

  • 将包引用添加到 Microsoft.AspNetCore.Grpc.JsonTranscoding
  • 通过添加 AddJsonTranscoding ,在服务器启动代码中注册转码。 例如, services.AddGrpc().AddJsonTranscoding()
  • 在包含 .csproj 文件的项目目录中创建目录结构 /google/api
  • google/api/http.proto google/api/annotations.proto 文件添加到 /google/api 目录中。
  • 用 HTTP 绑定和路由在 .proto 文件中注释 gRPC 方法:
  • syntax = "proto3";
    import "google/api/annotations.proto";
    package greet;
    service Greeter {
      rpc SayHello (HelloRequest) returns (HelloReply) {
        option (google.api.http) = {
          get: "/v1/greeter/{name}"
    message HelloRequest {
      string name = 1;
    message HelloReply {
      string message = 1;
    

    SayHello gRPC 方法现在可以作为 gRPC 和 JSON Web API 调用:

  • 请求: GET /v1/greeter/world
  • 响应: { "message": "Hello world" }
  • 如果服务器配置为针对每个请求写入日志,则服务器日志显示 gRPC 服务执行 HTTP 调用。 转码将传入的 HTTP 请求映射到 gRPC 消息,然后将响应消息转换为 JSON。

    info: Microsoft.AspNetCore.Hosting.Diagnostics[1]
          Request starting HTTP/1.1 GET https://localhost:5001/v1/greeter/world
    info: Microsoft.AspNetCore.Routing.EndpointMiddleware[0]
          Executing endpoint 'gRPC - /v1/greeter/{name}'
    info: Server.GreeterService[0]
          Sending hello to world
    info: Microsoft.AspNetCore.Routing.EndpointMiddleware[1]
          Executed endpoint 'gRPC - /v1/greeter/{name}'
    info: Microsoft.AspNetCore.Hosting.Diagnostics[2]
          Request finished in 1.996ms 200 application/json
    

    批注 gRPC 方法

    gRPC 方法必须在支持转码之前使用 HTTP 规则进行注释。 HTTP 规则包括有关如何调用 gRPC 方法的信息,例如 HTTP 方法和路由。

    service Greeter {
      rpc SayHello (HelloRequest) returns (HelloReply) {
        option (google.api.http) = {
          get: "/v1/greeter/{name}"
    

    继续浏览示例:

  • 使用 SayHello 方法定义 Greeter 服务。 该方法具有使用名称 google.api.http 指定的 HTTP 规则。
  • 可以使用 GET 请求和 /v1/greeter/{name} 路由访问该方法。
  • 请求消息上的 name 字段绑定到路由参数。
  • 许多选项可用于自定义 gRPC 方法如何绑定到 RESTful API。 有关批注 gRPC 方法和自定义 JSON 的详细信息,请参阅为 gRPC JSON 转码配置 HTTP 和 JSON

    流式处理方法

    HTTP/2 上的传统 gRPC 支持所有方向的流式处理。 转码仅限于服务器流式处理。 不支持客户端流式处理和双向流式处理方法。

    服务器流式处理方法使用以行分隔的 JSON。 使用 WriteAsync 的每条消息都会序列化为 JSON,后跟一个新行。

    以下服务器流式处理方法写入三条消息:

    public override async Task StreamingFromServer(ExampleRequest request,
        IServerStreamWriter<ExampleResponse> responseStream, ServerCallContext context)
        for (var i = 1; i <= 3; i++)
            await responseStream.WriteAsync(new ExampleResponse { Text = $"Message {i}" });
            await Task.Delay(TimeSpan.FromSeconds(1));
    

    客户端接收三个以行分隔的 JSON 对象:

    {"Text":"Message 1"}
    {"Text":"Message 2"}
    {"Text":"Message 3"}
    

    请注意,WriteIndentedJSON 设置不适用于服务器流式处理方法。 整齐打印将新行和空格添加到 JSON,这不能与以行分隔的 JSON 一起使用。

    HTTP 协议

    .NET SDK 中包含的 ASP.NET Core gRPC 服务模板创建仅针对 HTTP/2 配置的应用。 当应用仅支持传统的 gRPC over HTTP/2 时,HTTP/2 是很好的默认设置。 但是,转码同时适用于 HTTP/1.1 和 HTTP/2。 某些平台(如 UWP 或 Unity)无法使用 HTTP/2。 若要支持所有客户端应用,请将服务器配置为启用 HTTP/1.1 和 HTTP/2。

    更新 appsettings.json 中的默认协议:

    "Kestrel": { "EndpointDefaults": { "Protocols": "Http1AndHttp2"

    或者,在启动代码中配置 Kestrel 终结点

    在同一端口上启用 HTTP/1.1 和 HTTP/2 需要 TLS 进行协议协商。 有关在 gRPC 应用中配置 HTTP 协议的详细信息,请参阅 ASP.NET Core gRPC 协议协商

    gRPC JSON 转码与 gRPC-Web

    转码和 gRPC-Web 都支持从浏览器调用 gRPC 服务。 但是,它们的操作方式是不同的:

  • gRPC-Web 允许浏览器应用通过 gRPC-Web 客户端和 Protobuf 从浏览器调用 gRPC 服务。 gRPC-Web 需要浏览器应用生成 gRPC 客户端,并且具有快速发送小型 Protobuf 消息的优点。
  • 转码允许浏览器应用调用 gRPC 服务,就像它们是使用 JSON 的 RESTful API 一样。 浏览器应用不需要生成 gRPC 客户端或了解 gRPC 的任何信息。
  • 可以使用浏览器 JavaScript API 调用以前的 Greeter 服务:

    var name = nameInput.value;
    fetch('/v1/greeter/' + name)
      .then((response) => response.json())
      .then((result) => {
        console.log(result.message);
        // Hello world
    

    grpc-gateway

    grpc-gateway 是从 gRPC 服务创建 RESTful JSON API 的另一种技术。 它使用相同的 .proto 注释将 HTTP 概念映射到 gRPC 服务。

    grpc-gateway 使用代码生成来创建反向代理服务器。 反向代理将 RESTful 调用转换为 gRPC+Protobuf,然后通过 HTTP/2 将调用发送到 gRPC 服务。 这种方法的优点是 gRPC 服务不知道 RESTful JSON API。 任何 gRPC 服务器都可以使用 grpc-gateway。

    同时,gRPC JSON 转码在 ASP.NET Core 应用内运行。 它将 JSON 反序列化为 Protobuf 消息,然后直接调用 gRPC 服务。 ASP.NET Core 中的转码为 .NET 应用开发人员提供了以下优势:

  • 复杂性更低:gRPC 服务和映射的 RESTful JSON API 都在同一个 ASP.NET Core 应用外部运行。
  • 性能更佳:转码将 JSON 反序列化为 Protobuf 消息,并直接调用 gRPC 服务。 与对不同的服务器进行新的 gRPC 调用相比,执行此进程内操作时性能有显著的优势。
  • 成本更低:减少了服务器数量,降低了每月托管费用。
  • 如需了解 grpc-gateway 的安装和使用,请参阅 grpc-gateway 自述文件

  • 为 gRPC JSON 转码 ASP.NET Core 应用配置 HTTP 和 JSON
  • 将 OpenAPI 与 gRPC JSON 转码 ASP.NET Core 应用配合使用
  • 在浏览器应用中使用 gRPC
  • ASP.NET Core gRPC 应用中的 gRPC-Web
  •