HTTP/2 上的 gRPC:构建健壮、高性能协议
在上一篇文章中,我们探讨了 HTTP/2 如何通过提供长连接框架来显著提高网络效率并实现实时通信。在本文中,我们将探讨 gRPC 如何基于 HTTP/2 的长连接来构建一个高性能、健壮的跨服务通信平台。我们将深入研究 gRPC 和 HTTP/2 之间的关系,gRPC 如何管理 HTTP/2 连接,以及 gRPC 如何利用 HTTP/2 来保持连接的活跃、健康和有效利用。
gRPC 语义
首先,我们来看看 gRPC 概念与 HTTP/2 概念之间的关系。gRPC 引入了三个新概念:通道1、远程过程调用(RPCs)和消息。三者之间的关系很简单:每个通道可以有许多 RPC,而每个 RPC 可以有许多消息。


我们来看看 gRPC 语义与 HTTP/2 的关系


通道是 gRPC 中的一个关键概念。HTTP/2 中的流(streams)允许在单个连接上进行多个并发对话;通道通过在多个并发连接上启用多个流来扩展了这一概念。表面上,通道为用户发送消息提供了简单的接口;然而,在底层,大量的工程工作用于保持这些连接的活跃、健康和有效利用。
通道表示到某个端点的虚拟连接,而实际上这些连接可能由许多 HTTP/2 连接支持。RPC 与一个连接相关联(这种关联将在后面详细描述)。RPC 在实践中就是普通的 HTTP/2 流。消息与 RPC 相关联,并作为 HTTP/2 数据帧发送。更具体地说,消息是分层在数据帧之上的。一个数据帧可以包含许多 gRPC 消息,或者如果一个 gRPC 消息相当大2,它可能会跨越多个数据帧。
解析器与负载均衡器
为了保持连接的活跃、健康和有效利用,gRPC 利用了许多组件,其中最重要的是名称解析器和负载均衡器。解析器将名称转换为地址,然后将这些地址交给负载均衡器。负载均衡器负责从这些地址创建连接并在连接之间对 RPC 进行负载均衡。




例如,一个 DNS 解析器可能会将某个主机名解析为 13 个 IP 地址,然后一个 RoundRobin 负载均衡器可能会创建 13 个连接——每个地址一个——并在每个连接上进行轮询 RPC。一个更简单的负载均衡器可能只创建到第一个地址的连接。或者,如果用户想要多个连接但知道主机名只会解析为一个地址,他们可以让负载均衡器对每个地址创建 10 次连接,以确保使用多个连接。
解析器和负载均衡器解决了 gRPC 系统中一些虽小但关键的问题。这种设计是故意的:将问题空间缩小到几个小而离散的问题有助于用户构建自定义组件。这些组件可用于微调 gRPC 以适应每个系统的个性化需求。
连接管理
一旦配置完成,gRPC 将保持由解析器和均衡器定义的连接池的健康、活跃和有效利用。
当连接失败时,负载均衡器将使用最后已知的地址列表3开始重新连接。同时,解析器将开始尝试重新解析主机名列表。这在许多场景中都很有用。例如,如果代理不再可达,我们希望解析器更新地址列表以排除该代理的地址。再举一个例子:DNS 条目可能会随时间变化,因此地址列表可能需要定期更新。通过这种方式和其它方式,gRPC 被设计为具有长期弹性。
解析完成后,负载均衡器会收到新地址的通知。如果地址已更改,负载均衡器可能会关闭在新列表中不存在的地址的连接,或创建到以前不存在的地址的连接。
识别失败的连接
gRPC 连接管理的有效性取决于其识别失败连接的能力。连接失败通常有两种类型:干净失败(其中失败被通信)和不那么干净的失败(其中失败未被通信)。
让我们考虑一个干净、易于观察的故障。干净故障可能发生在端点有意终止连接时。例如,端点可能已正常关闭,或者计时器已超时,促使端点关闭连接。当连接干净关闭时,TCP 语义就足够了:关闭连接会导致 FIN 握手发生。这会结束 HTTP/2 连接,进而结束 gRPC 连接。gRPC 将立即开始重新连接(如上所述)。这非常干净,不需要额外的 HTTP/2 或 gRPC 语义。
不那么干净的版本是端点在未通知客户端的情况下死亡或挂起。在这种情况下,TCP 可能需要长达 10 分钟的重试才能将连接视为失败。当然,10 分钟未能识别连接已死亡是不可接受的。gRPC 使用 HTTP/2 语义解决了这个问题:当使用 KeepAlive 配置时,gRPC 会定期发送 HTTP/2 PING 帧。这些帧绕过流量控制,用于确定连接是否活跃。如果在及时时间内未返回 PING 响应,gRPC 将认为连接失败,关闭连接,并开始重新连接(如上所述)。
通过这种方式,gRPC 保持一个健康连接池,并使用 HTTP/2 定期检查连接的健康状况。所有这些行为对用户都是透明的,消息重定向会自动实时发生。用户只需在看似始终健康的连接池上发送消息。
保持连接活跃
如前所述,KeepAlive 提供了一个宝贵的优势:通过发送 HTTP/2 PING 定期检查连接的健康状况,以确定连接是否仍然活跃。然而,它还有另一个同样有用的优势:向代理发送活跃信号。
考虑一个客户端通过代理向服务器发送数据。客户端和服务器可能乐意无限期地保持连接活跃,并根据需要发送数据。另一方面,代理通常资源受限,可能会为了节省资源而终止空闲连接。Google Cloud Platform (GCP) 负载均衡器在 10 分钟后会断开看似空闲的连接,而 Amazon Web Services Elastic Load Balancers (AWS ELBs) 则在 60 秒后断开它们。
gRPC 定期在连接上发送 HTTP/2 PING 帧,从而创建了非空闲连接的感知。使用上述空闲终止规则的端点将不会终止这些连接。
一个强大、高性能的协议
HTTP/2 为长连接、实时通信流提供了基础。gRPC 在此基础上构建了连接池、健康语义、数据帧和多路复用的高效利用,以及 KeepAlive。
选择协议的开发人员必须选择能够满足当前和未来需求的协议。选择 gRPC 将会受益匪浅,无论是为了弹性、性能、长短期通信、可定制性,还是仅仅为了知道其协议能够扩展到极其庞大的流量,并在此过程中始终保持高效。要立即开始使用 gRPC 和 HTTP/2,请查看 gRPC 的入门指南。