RSS

gRPC 负载均衡

本文介绍了在部署 gRPC 时会遇到的各种负载均衡场景。如果您使用 gRPC 且后端有多个实例,那么本文正是为您准备的。

大规模 gRPC 部署通常包含多个相同的后端实例和多个客户端。每个服务器都有一定的容量。负载均衡用于将客户端的负载以最优方式分发到可用服务器上。

为什么选择 gRPC?

gRPC 是一种在 HTTP/2 之上实现的现代 RPC 协议。HTTP/2 是第 7 层(应用层)协议,运行在 TCP(第 4 层 - 传输层)协议之上,而 TCP 又运行在 IP(第 3 层 - 网络层)协议之上。与传统的 HTTP/REST/JSON 机制相比,gRPC 具有许多优势,例如:

  1. 二进制协议 (HTTP/2)
  2. 单连接多路复用请求 (HTTP/2)
  3. 头部压缩 (HTTP/2)
  4. 强类型服务与消息定义 (Protobuf)
  5. 多种语言下惯用的客户端/服务器库实现

此外,gRPC 还能与服务发现、名称解析、负载均衡器、追踪和监控等生态系统组件无缝集成。

负载均衡选项

代理还是客户端侧?

注:在某些文献中,代理负载均衡也称为服务端负载均衡。

在代理负载均衡和客户端侧负载均衡之间做出选择是一项主要的架构决策。在代理负载均衡中,客户端将 RPC 发送到负载均衡 (LB) 代理。LB 将 RPC 调用分发给实现实际业务逻辑的某个可用后端服务器。LB 会追踪每个后端的负载,并实现算法以公平地分配负载。客户端本身不需要了解后端服务器。客户端可以是不可信的。这种架构通常用于面向用户的服务,即来自开放互联网的客户端可以连接到数据中心内的服务器,如下图所示。在此场景中,客户端向 LB 发送请求 (#1)。LB 将请求传递给其中一个后端 (#2),后端向 LB 报告负载 (#3)。

image alt text

在客户端侧负载均衡中,客户端了解多个后端服务器,并为每个 RPC 选择一个服务器。客户端从后端服务器获取负载报告,并由客户端执行负载均衡算法。在更简单的配置中,可以不考虑服务器负载,客户端只需在可用服务器之间进行轮询 (round-robin)。这如下图所示。如您所见,客户端向特定后端发起请求 (#1)。后端返回负载信息 (#2),通常是在执行客户端 RPC 的同一个连接上。客户端随后更新其内部状态。

image alt text

下表概述了每种模式的优缺点。

代理 (Proxy)客户端侧 (Client Side)
优点
  • 客户端简单
  • 客户端无需感知后端
  • 适用于不可信客户端
  • 性能高(消除了额外的跳转)
缺点
  • LB 处于数据路径中
  • 延迟较高
  • LB 吞吐量可能限制可扩展性
  • 客户端复杂
  • 客户端需要追踪服务器负载和健康状态
  • 客户端需要实现负载均衡算法
  • 各语言实现和维护负担较重
  • 客户端需要是可信的,或者信任边界需要由旁路 LB 处理。

代理负载均衡选项

代理负载均衡可以是 L3/L4(传输层)或 L7(应用层)。在传输层负载均衡中,服务器终止 TCP 连接并打开与目标后端的另一个连接。应用数据(HTTP/2 和 gRPC 帧)只是在客户端连接和后端连接之间进行复制。L3/L4 LB 设计上处理极少,相比 L7 LB 增加了更少的延迟,并且更具成本效益,因为它消耗的资源更少。

在 L7(应用层)负载均衡中,LB 会终止并解析 HTTP/2 协议。LB 可以检查每个请求并根据请求内容分配后端。例如,作为 HTTP 头部一部分发送的会话 cookie 可用于关联特定后端,从而确保该会话的所有请求都由同一后端提供服务。一旦 LB 选择了合适的后端,它就会创建一个到该后端的新 HTTP/2 连接。然后,它将从客户端接收到的 HTTP/2 流转发给选定的后端。利用 HTTP/2,LB 可以将来自一个客户端的流分发到多个后端。

L3/L4(传输层)与 L7(应用层)

用例建议
不同连接间的 RPC 负载差异很大使用应用层 LB
存储或计算亲和性很重要使用应用层 LB,并利用 cookie 或类似机制将请求路由到正确的后端
在代理中最小化资源利用比功能更重要使用 L3/L4 LB
延迟至关重要使用 L3/L4 LB

客户端侧负载均衡选项

胖客户端 (Thick client)

“胖客户端”方法意味着负载均衡逻辑在客户端实现。客户端负责追踪可用服务器及其工作负载,以及选择服务器所使用的算法。客户端通常会集成与其他基础设施(如服务发现、名称解析、配额管理等)通信的库。

旁路 (Lookaside) 负载均衡

注:旁路负载均衡器也称为外部负载均衡器或单臂负载均衡器。

通过旁路负载均衡,负载均衡逻辑在专门的 LB 服务器中实现。客户端查询旁路 LB,LB 返回可使用的最佳服务器。保留服务器状态和实现 LB 算法的繁重工作集中在旁路 LB 中。注意,客户端也可以选择在 LB 实现的复杂算法之上执行简单的算法。gRPC 为使用此模型的客户端与 LB 之间的通信定义了协议。详情请参阅 gRPC 中的负载均衡 文档

下图演示了这种方法。客户端从旁路 LB 获取至少一个地址 (#1)。然后客户端使用此地址发起 RPC (#2),服务器向 LB 发送负载报告 (#3)。旁路 LB 与名称解析、服务发现等其他基础设施进行通信 (#4)。

image alt text

建议与最佳实践

根据具体的部署和约束条件,我们建议如下:

设置建议
  • 客户端与服务器之间流量极高
  • 客户端可信
  • 胖客户端侧负载均衡
  • 使用 ZooKeeper/Etcd/Consul/Eureka 的客户端侧 LB。ZooKeeper 示例
  • 传统设置 - 许多客户端连接到代理后的服务
  • 需要在服务器和客户端之间建立信任边界
  • 代理负载均衡
  • 带 GCLB 的 L3/L4 LB(如果使用 GCP)
  • 带 haproxy 的 L3/L4 LB - 配置文件
  • Nginx 即将推出
  • 如果需要会话粘性 - 使用 Envoy 作为代理的 L7 LB
  • 微服务 - 数据中心内 N 个客户端,M 个服务器
  • 极高的性能要求(低延迟、高流量)
  • 客户端不可信
  • 旁路负载均衡
  • 使用 gRPC-LB 协议 的客户端侧 LB。自行实现(2017 年第二季度),托管 gRPC-LB 正在开发中。
  • 使用 Linkerd 或 Istio 等现有的服务网格设置
  • 服务网格 (Service Mesh)
  • 使用 IstioEnvoy 的内置 LB。