Spring Cloud Gateway 和 gRPC

工程 | Alberto C. Ríos | 2021 年 12 月 8 日 | ...

从 Spring Cloud 2021.0.0(又名 Jubilee)发布列车的一部分的 3.1.0 版本开始,Spring Cloud Gateway 包含了对 gRPC 和 HTTP/2 的支持。

我们将介绍 gRPC 背后的基本概念以及如何通过两个示例对其进行配置

  • 一个展示了 Spring Cloud Gateway 如何能够透明地重新路由 gRPC 流量,而无需了解 proto 定义,也无需修改我们现有的 gRPC 服务器。

  • 另一个展示了我们如何在 Spring Cloud Gateway 中创建自定义过滤器以将 JSON 有效负载转换为 gRPC 消息。

gRPC 和 HTTP/2 简介

HTTP/2 使我们的应用程序更快、更简单、更健壮。通过启用请求和响应多路复用、添加 HTTP 标头字段的有效压缩以及添加对请求优先级和服务器推送的支持来减少延迟。

在提高 HTTPS 性能时,连接数量的减少尤其重要:这样我们就可以减少昂贵的 TLS 握手,更有效地重用会话,从而减少客户端和服务器资源。

HTTP/2 提供了两种协商应用程序级协议的机制

  • H2C 使用明文支持 HTTP/2.0

  • H2 使用 TLS 支持 HTTP/2.0

即使 reactor-netty 支持 H2C 明文协议,Spring Cloud Gateway 也需要使用 TLS 的 H2 来确保传输安全。

HTTP/2 添加了一个二进制帧层,这是 HTTP 消息在客户端和服务器之间封装和传输的方式,从而能够更有效地传输数据。

感谢 reactor-netty 及其 HTTP/2 支持,我们能够扩展 Spring Cloud Gateway 以支持 gRPC。

gRPC 是一种高性能远程过程调用框架,可以在任何环境中运行。它提供双向流,并且基于 HTTP/2。

gRPC 服务可以使用 Protocol Buffers(一种强大的二进制序列化工具集和语言)定义,并提供跨不同语言生成客户端和服务器的工具。

入门

为了在 Spring Cloud Gateway 中启用 gRPC,我们需要在我们的项目中启用 HTTP/2 和 SSL 通过添加密钥库,这可以通过添加以下内容进行配置

server:
  http2:
    enabled: true
  ssl:
    key-store-type: PKCS12
    key-store: classpath:keystore.p12
    key-store-password: password
    key-password: password
    enabled: true

现在我们已经启用了它,我们可以创建一个路由将流量重定向到 gRPC 服务器并利用现有的过滤器和谓词,例如,此路由将重定向来自以 grpc 开头的任何路径的流量到端口 6565 上的本地服务器,并添加值为 header-value 的标头 X-Request-header

spring:
  cloud:
    gateway:
      routes:
        - id: grpc
          uri: https://127.0.0.1:6565
          predicates:
            - Path=/grpc/**
          filters:
            - AddResponseHeader=X-Request-header, header-value

运行 gRPC 到 gRPC

可以在以下部分的此存储库中找到端到端示例

grpc-simple-gateway

  • 一个 grpc-server,它公开了一个 HelloService 和一个 gRPC 端点以接收 HelloRequest 并返回 HelloResponse

syntax = "proto3";

message HelloRequest { string firstName = 1; string lastName = 2; }

message HelloResponse { string greeting = 1; }

service HelloService { rpc hello(HelloRequest) returns (HelloResponse); }

服务器将连接一个问候语与 firstNamelastName,并使用 greeting 进行响应。

例如,此输入

firstName: Saul
lastName: Hudson

将输出

greeting: Hello, Saul Hudson
  • 一个 grpc-client,负责将 HelloRequest 发送到 HelloService

  • grpc-simple-gateway 路由请求并添加一个标头,其中包含上面提到的配置。请注意,此网关应用程序没有任何对 gRPC 或客户端和服务器使用的 proto 定义的依赖关系。

目前只有一个路由将所有内容转发到 grpc-server

      routes:
        - id: grpc
          uri: https://127.0.0.1:6565
          predicates:
            - Path=/**
          filters:
            - AddResponseHeader=X-Request-header, header-value

启动将侦听请求的服务器

 ./gradlew :grpc-server:bootRun

然后,我们启动将重新路由 gRPC 请求的网关

./gradlew :grpc-simple-gateway:bootRun

最后,我们可以使用指向网关应用程序的客户端

./gradlew :grpc-client:bootRun

可以在 grpc-simple-gateway/src/main/resources/application.yaml 中修改网关路由和过滤器

使用自定义过滤器运行 JSON 到 gRPC

由于 Spring Cloud Gateway 的灵活性,可以创建自定义过滤器以将 JSON 有效负载转换为 gRPC 消息。

尽管由于我们必须在网关中序列化和反序列化请求并从中创建通道,因此会对性能产生影响,但如果您希望公开 JSON API 同时保持内部兼容性,这是一种常见模式。

为此,我们可以扩展我们的 grpc-json-gateway 以包含我们要发送的消息的 proto 定义。

grpc-json-gateway

Spring Cloud Gateway 包含一个创建自定义过滤器的机制,允许我们拦截请求并向其中添加自定义逻辑。

对于这种情况,我们将反序列化 JSON 请求并创建一个 gRPC 通道,该通道将向 grpc-server 发送消息。

static class GRPCResponseDecorator extends ServerHttpResponseDecorator {

  @Override
  public Mono<Void> writeWith(Publisher<?extends DataBuffer> body) {
    exchange.getResponse().getHeaders().set("Content-Type", "application/json");

    URI requestURI = exchange.getRequest().getURI();
    ManagedChannel channel = createSecuredChannel(requestURI.getHost(), 6565);

    return getDelegate().writeWith(deserializeJSONRequest()
            .map(jsonRequest -> {
                String firstName = jsonRequest.getFirstName();
                String lastName = jsonRequest.getLastName();
                return HelloServiceGrpc.newBlockingStub(channel)
                        .hello(HelloRequest.newBuilder()
                                .setFirstName(firstName)
                                .setLastName(lastName)
                                .build());
            })
            .map(this::serialiseJSONResponse)
            .map(wrapGRPCResponse())
            .cast(DataBuffer.class)
            .last());
  }
}

完整的实现可以在:grpc-json-gateway/src/main/java/com/example/grpcserver/hello/JSONToGRPCFilterFactory.java 中找到

使用相同的 grpc-server,我们可以使用以下命令启动带有自定义过滤器的网关

./gradlew :grpc-json-gateway:bootRun

并使用例如 curl 将 JSON 请求发送到 grpc-json-gateway

curl -XPOST 'https://127.0.0.1:8091/json/hello' -d '{"firstName":"Duff","lastName":"McKagan"}' -k -H"Content-Type: application/json" -v

我们看到网关应用程序如何转发请求并使用新的 Content-Type 标头返回 JSON 有效负载

< HTTP/2 200
< content-type: application/json
< content-length: 34
<
* Connection #0 to host localhost left intact
{"greeting":"Hello, Duff McKagan"}

后续步骤

在这篇文章中,我们了解了如何在 Spring Cloud Gateway 中集成 gRPC 的一些示例。我很想知道您在您的经验中发现了哪些其他有用的用法。

获取 Spring 时事通讯

与 Spring 时事通讯保持联系

订阅

领先一步

VMware 提供培训和认证,以加速您的进步。

了解更多

获得支持

Tanzu Spring 在一个简单的订阅中提供对 OpenJDK™、Spring 和 Apache Tomcat® 的支持和二进制文件。

了解更多

即将举行的活动

查看 Spring 社区中所有即将举行的活动。

查看全部