领先一步
VMware 提供培训和认证,以加速您的进步。
了解更多从 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 消息。
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-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); }
服务器将连接一个问候语与 firstName
和 lastName
,并使用 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 中修改网关路由和过滤器
由于 Spring Cloud Gateway 的灵活性,可以创建自定义过滤器以将 JSON 有效负载转换为 gRPC 消息。
尽管由于我们必须在网关中序列化和反序列化请求并从中创建通道,因此会对性能产生影响,但如果您希望公开 JSON API 同时保持内部兼容性,这是一种常见模式。
为此,我们可以扩展我们的 grpc-json-gateway
以包含我们要发送的消息的 proto
定义。
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 的一些示例。我很想知道您在您的经验中发现了哪些其他有用的用法。