Spring Cloud Gateway 和 gRPC

工程 | 阿尔贝托·C·里奥斯 | 2021年12月8日 | ...

从3.1.0版本开始,作为 Spring Cloud 2021.0.0(又名 Jubilee)发布列车的一部分,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 定义,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-valueX-Request-header

spring:
  cloud:
    gateway:
      routes:
        - id: grpc
          uri: https://: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,负责向 HelloService 发送 HelloRequest

  • grpc-simple-gateway 负责路由请求并添加上述配置的头。请注意,此网关应用程序不依赖于 gRPC,也不依赖于客户端和服务器使用的 proto 定义。

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

      routes:
        - id: grpc
          uri: https://: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

并使用例如 curlgrpc-json-gateway 发送 JSON 请求

curl -XPOST 'https://: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"}

下一步

在这篇文章中,我们探讨了 gRPC 如何集成到 Spring Cloud Gateway 的几个示例。我很想知道您在实践中还发现了哪些其他有用的用法。

获取 Spring 新闻通讯

通过 Spring 新闻通讯保持联系

订阅

领先一步

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

了解更多

获得支持

Tanzu Spring 提供 OpenJDK™、Spring 和 Apache Tomcat® 的支持和二进制文件,只需一份简单的订阅。

了解更多

即将举行的活动

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

查看所有