《SRE google 运维解密》读书笔记 (六)

负载均衡

前端

使用 DNS 进行负载均衡。在 DNS 回复中提供多个 A 记录或者 AAAA 记录。
虽然 DNS 看起来简单,但是存在不少问题。

  1. DNS 对客户端行为的约束很弱:记录是随机选择的。
  2. 客户端无法识别“最近”的地址
  3. 权威服务器不能主动清楚某个解析器的缓存,DNS 记录需要保持一个相对低的失效值(TTL)。

需要在 DNS 负载后面增加一层虚拟 IP 地址,我们常说的 VIP。

使用 VIP 进行负载均衡
虚拟 IP(VIP) 不是绑定在某一个特定的网络接口上的。很多设备共享。外界看 VIP 是一个独立的普通 IP。VIP 是网络负载均衡器。负载均衡器接收网络数据包,转发给 背后的某个服务器。

负载的方案:

  • 对于无状态的服务,理论上说永远优先负载最小的后端服务
  • 对于有转态的服务
    • 某个连接标识取模
    • 一致性哈希

后端

理想情况

某个服务的负载会完全均匀的分发给所有的后端服务。任何时间点,最忙和最不忙的任务消耗相同数量的 CPU。

识别异常任务

  • 限流
    • 客户端限流
    • 某个后端的活跃请求达到一定数量,客户端将后端标记为异常转态,不再发送请求。
    • 正常情况下,后端请求很快完成,限流几乎不会触发
    • 后端过载了,请求响应慢,客户端就会自动避开这个后端
    • 缺点就是,不精确。后端很可能达到限额之前就过载了。反之亦然。
  • 坡脚鸭任务
    • 客户端视角来看,后端任务有以下几个状态
      • 健康
      • 拒绝连接
      • 坡脚鸭状态
        • 后端服务正常,也能服务请求。但是明确要求客户端停止发送请求。
        • 某个请求进入坡脚鸭状态,需要广播给客户端
        • 处于停止过程中的服务不会给正在处理的请求返回错误
        • 可以实现优雅的下线服务

利用划分子集限制连接池大小

子集划分:限制某个客户端任务需要连接的后端数量。

Google 的 RPC 框架对于每个客户端都会维持一个长连接。如果一个集群的规模过大,客户端就要维护很多长连接。

子集选择算法

  • 随机选择
  • 确定性算法

负载均衡策略

简单轮询

造成效果差的因素如下:

  1. 子集过小
  2. 请求处理的成本不同
  3. 物理服务器的差异
  4. 无法预支的性能因素
    1. 怀邻居(物理服务器上的其他进程)
    2. 任务重启

最闲轮询策略

客户端追踪子集中每个后端任务的活跃请求数量,在活跃请求最小的任务中进行轮询。

最危险的坑:如果一个任务不健康,可能 100% 返回错误。取决于错误的类型,错误回复可能延迟非常低。从而给异常任务分配的大量的请求。
需要将错误信息计算为活跃请求,剔除异常任务。

限制:

  • 活跃的请求数量不一定是后端容量的代表
  • 每个客户端的活跃请求不包括其他客户端发往同一个后端的请求

实践中发现,效果很差。

加权轮询

每个客户端为子集中的每个后端任务保持一个“能力”值。请求仍以轮询方式分发,客户端按照能力值权重比例调节。

实践中效果较好。