Subscribe to receive notifications of new posts:

gRPC 之路

2020-10-26

11 min read

在 2020 年生日周期间,Cloudflare 发布了对 gRPC® 的支持。大家对 Beta 测试的浓厚兴趣令我们受宠若惊,在此我们向所有申请并试用 gRPC 的人致以衷心感谢!在本文中,我们将深入探讨有关如何实施这一支持的技术细节。

什么是 gRPC?

gRPC 是通过 HTTP/2 运行的一种开源 RPC 框架。RPC(远程过程调用)是供计算机用于命令另一台计算机执行某项操作,而不是调用库中的本地函数。长久以来,分布式计算发展过程中一直都有 RPC 的身影,出现了针对不同领域的不同实现。以下特征造就了 gRPC 的与众不同:

  • 需要现已广泛使用的现代 HTTP/2 协议进行传输。
  • 开源形式提供完整的客户端/服务器参考实现、演示和测试套件。
  • 不指定消息格式,但协议缓冲区是首选的序列化机制。
  • 客户端和服务器都可流式传输数据,避免了轮询新数据或创建新连接的麻烦。

在协议方面,gRPC 广泛使用HTTP/2 帧:请求和响应看起来与普通的 HTTP/2 请求非常相似。

不过,与众不同的地方在于 gRPC 使用了 HTTP 尾部(trailer)。HTTP 尾部尽管没有得到广泛使用,自 1999 年于原始 HTTP/1.1 RFC2616中定义以来一直存在。根据定义,HTTP 消息标头位于 HTTP 消息正文之前,但 HTTP 尾部是一组可附加到消息正文后面的 HTTP 标头。不过,由于 HTTP 尾部的用例不多,因此许多服务器和客户端实施没有全面支持它们。虽然 HTTP/1.1 需要为其正文使用分块传输编码来发送 HTTP 尾部,但对于 HTTP/2,尾部位于正文的 DATA 帧之后的 HEADER 帧中。

在一些情形中,HTTP 尾部很有用处。例如,我们使用 HTTP 响应代码指示请求的状态,但响应代码是 HTTP 响应的第一行,因此需要尽早确定响应代码。HTTP 尾部可以在正文之后发送一些元数据。例如,假设您的 Web 服务器发送了一个大数据流(非固定大小),最后您要发送所发送数据的 SHA256 校验和,以便客户端可以验证内容。通常,这无法通过 HTTP 状态代码或应在响应开始时发送的响应标头来实现。使用 HTTP 尾部标头,您可以在发送完所有数据后发送另一个标头(例如 Digest)。

gRPC 将 HTTP 尾部用于两个用途。首先,在发送内容后以尾部标头形式发送最终状态(grpc-status)。其二是为了支持流式用例。这些用例的持续时间远比普通 HTTP 请求长。HTTP 尾部用于提供请求或响应的后处理结果。例如,如果在流数据处理过程中发生错误,则可使用尾部发送错误代码,而这无法通过消息正文之前的标头来实现。

以下是 HTTP/2 帧中 gRPC 请求和响应的简单示例:

向 Cloudflare Edge 添加 gRPC 支持

既然 gRPC 使用 HTTP/2,原生支持 gRPC 听起来不难,因为 Cloudflare 已经支持 HTTP/2 了。但是,我们有两个问题:

  • 我们的边缘代理不完全支持 HTTP 请求/响应尾部标头:Cloudflare 使用 NGINX 接受来自用户端的流量,并且对尾部的支持有限。让情况更加复杂的是,穿过 Cloudflare 的请求和响应还需要经过一系列其他代理。
  • HTTP/2 至源站:我们的边缘代理使用 HTTP/1.1 从源站获取对象(无论是动态还是静态对象)。要代理 gRPC 流量,我们需要支持使用 HTTP/2 与客户 gRPC 源站连接。
  • gRPC 流式传输需要允许双向请求/响应流:gRPC 有两种类型的协议流;其一是一元的,即简单的请求和响应,其二是流式传输,允许各个方向上不间断的数据流。为了全面支持流式传输,需要在另一端接收到响应标头之后发送 HTTP 消息正文。例如,流式传输客户端将在接收到响应标头之后持续发送请求正文。

出于这些原因,gRPC 请求通过我们的网络代理时会中断。为克服这些限制,我们研究了各种各样的解决方案。例如,NGINX 具有一个 gRPC 上游模块,可支持 HTTP/2 gRPC 源站,但它是一个单独的模块,而且还需要 HTTP/2 下游,不能用于我们的服务,因为请求在某些情况下会穿过多个 HTTP 代理。由于内部负载平衡架构的关系,而且确保所有内部流量都使用 HTTP/2 将花费太多精力,因此在管道的每一处全部使用 HTTP/2 是不现实的。

转换成 HTTP/1.1?

最终,我们找到了一个比较好的办法:在内部网络中将 gRPC 消息转换为不含尾部的 HTTP/1.1 消息,然后在请求发送到源站之前将转换回 HTTP/2。对于 Cloudflare 中不支持 HTTP 尾部的大多数 HTTP 代理,这个办法都适用,而且我们需要的更改也极少。

我们不必自己发明格式,gRPC 社区已经开发了一种 HTTP/1.1 兼容版本:gRPC-web。gRPC-web 是对基于 HTTP/2 的原始 gRPC 规范的修改。最初目的是与不能直接访问 HTTP/2 帧的 Web 浏览器搭配。使用 gRPC-web 时,HTTP 尾部将移到正文中,因此我们无需担心代理中对 HTTP 尾部的支持。它还附带了流式传输支持。我们的安全产品(例如 WAF 和 Bot Management)仍可以检查生成的 HTTP/1.1 消息,提供的安全级别与 Cloudflare 为其他 HTTP 流量带来的相同。

在 Cloudflare 的边缘代理接收到 HTTP/2 gRPC 消息时,该消息将“转换”为 HTTP/1.1 gRPC-web 格式。gRPC 消息转换之后,它将穿过我们的管道,并以和普通 HTTP 请求相同的方式应用 WAF、Cache 和 Argo 等服务。

在 gRPC-web 消息即将离开 Cloudflare 网络时,需要再次“转回”成 HTTP/2 gRPC。由我们系统转换的请求会加上标记,因此我们的系统不会意外转换源自客户端的 gRPC-web 流量。

HTTP/2 源站支持

一个工程方面的挑战是支持使用 HTTP/2 连接到源站。在这个项目之前,Cloudflare 无法通过 HTTP/2 连接到源站。

因此,我们决定在内部构建 HTTP/2 源站支持。我们开发了一个独立的源代理,它能通过 HTTP/2 连接到源站。在这个新平台基础上,我们实施了 gRPC 的转换逻辑。gRPC 支持是利用此新平台的第一个功能。路线图中已经规划了对 HTTP/2 连接源服务器的更广泛支持。

gRPC 流式传输支持

如上文所述,gRPC 具有流式传输模式,可以在流中发送请求正文或响应正文。在 gRPC 请求的生命周期内,可以随时发送 gRPC 消息块。流的末尾有一个 HEADER 帧用来指示流末尾。转换成 gRPC-web 后,我们将使用分块编码发送正文并使连接保持开放以接受正文的两面,直到获得用来指示流末尾的 gRPC 消息块为止。这需要我们的代理支持双向传输。

例如,客户端流式传输是一种有趣的模式,其中服务器已经使用响应代码及其标头进行响应,但客户端仍然能够发送请求正文。

互操作性测试

Cloudflare 的每一新功能在发布之前都需要进行妥善测试。在最初开发过程中,我们使用了 envoy 代理及其 gRPC-web 过滤器功能以及 gRPC 的官方示例。我们准备了一个带有 envoy 代理和 gRPC 测试源站的测试环境,以确保边缘代理可以正确处理 gRPC 请求。来自 gRPC 测试客户端的请求发送到边缘代理,并转换为 gRPC-web,然后转发到 envoy 代理。之后,envoy 转换回 gRPC 请求并发送到 gRPC 测试源站。我们通过这种办法成功验证了基本行为。

基本功能准备就绪后,我们还需要确保两端的转换功能正常工作。为此,我们构建了更深层次的互操作性测试。

我们参照现有的 gRPC 互操作性测试用例开发了我们的测试套件,并在本地边缘代理和新源站代理之间运行了测试的第一个迭代。

对于测试的第二个迭代,我们使用了不同的 gRPC 实施。例如,某些服务器在出现即时错误时以仅尾部响应发送最终状态(grpc-status)。此响应在单个 HEADERS 帧块中包含 HTTP/2 响应标头和尾部,并且同时设置了 END_STREAM 和 END_HEADERS 标记。其他实施则在单独的 HEADERS 帧中以尾部形式发送最终状态。

在本地验证了互操作性之后,我们针对支持生产环境中所有服务的开发环境运行了测试。这样,我们便可以确保没有意外副作用会影响 gRPC 请求。

我们喜欢开展内部测试!我们成功部署了边缘 gRPC 支持的第一批服务,其中之一是 Cloudflare drand 随机信标。启用不难,而且我们过去几周一直在生产环境中运行该信标,从未出现任何问题。

总结

支持新协议是一项激动人心的工作!在现有系统中实施新技术支持既令人兴奋,又错综复杂,通常涉及在实施速度和整体系统复杂性之间进行权衡。对于 gRPC,我们成功地快速建立了支持,而且无需对 Cloudflare 边缘进行大幅更改。为实现这个目标,我们仔细考虑了各种实施方案,而后敲定了在 HTTP/2 gRPC 和 HTTP/1.1 gRPC-web 格式之间进行转换的方案。选择这种设计不仅加快并简化了服务集成,而且依然与我们用户的期望和限制相符。

如果您对使用 Cloudflare 保护和加速 gRPC 服务感兴趣,可以在此处阅读更多内容。另外,如果您想攻克一些有趣的工程挑战(如本文所述的这个),欢迎您报名申请

gRPC® The Linux Foundation 的注册商标。

We protect entire corporate networks, help customers build Internet-scale applications efficiently, accelerate any website or Internet application, ward off DDoS attacks, keep hackers at bay, and can help you on your journey to Zero Trust.

Visit 1.1.1.1 from any device to get started with our free app that makes your Internet faster and safer.

To learn more about our mission to help build a better Internet, start here. If you're looking for a new career direction, check out our open positions.
简体中文gRPC (CN)

Follow on X

Junho Choi|@junhochoi
Cloudflare|@cloudflare

Related posts