跳到主要内容

负载均衡器类型

负载均衡器按其工作的 OSI 层次可分为三类。选择的关键不是"哪种 LB 最好",而是"流量是什么形态,我需要做哪些路由决策?"

三大类别

DNS 轮询

客户端解析主机名并获得一个随机顺序的 IP 地址列表。通常它会尝试第一个,失败时降级到下一个。

  • 优点: 免费、配置简单、不需要维护基础设施。
  • 缺点: 各层的 DNS 缓存(解析器、OS、浏览器)使你无法迅速响应失败的主机。在你从轮询中移除主机后,客户端还会持有陈旧记录 TTL 分钟。没有健康感知——DNS 会一直返回死主机,直到记录被更新。
  • 何时使用: 在地理上分离的集群之间分发流量("把欧洲用户送到 eu.example.com"),或在区域 LB 前作第一跳。几乎绝不作为唯一层。

网络(L3/L4)负载均衡器

根据 IP 地址和端口路由流量。在 TCP/UDP 层工作,不检查 payload。

  • L3(网络层):IP 层路由。纯 L3 LB 罕见;大多数自称 L3 的产品实际是 L3+L4。
  • L4(传输层):TCP/UDP 会话层路由。例如:AWS NLB、TCP 模式的 HAProxy、LVS、Cloudflare Spectrum。

实际工作方式: 大多数 L4 LB 使用两种技术之一。直接服务器返回(DSR)重写目的 IP,数据包到达后端,但后端直接回复客户端、绕过 LB——这样可以承载巨量吞吐,因为 LB 只处理单向流量。NAT 模式重写双向数据包,LB 见到所有流量;更简单,但吞吐受限。

  • 优点: 极高的吞吐量(每秒数百万连接)。极低延迟(亚毫秒开销)。由于没有协议特定逻辑,对任何基于 TCP 的协议都能工作——HTTP、Redis、Kafka、自定义协议。
  • 缺点: 无法基于 HTTP header、路径或 cookie 做路由决策。健康检查只能检测"这个 TCP 端口是否在接受连接",这在应用已崩溃时也常常通过。
  • 何时使用: 非 HTTP 协议、对每包开销敏感的超高吞吐 HTTP,或作为 L7 LB 前的第一层。

应用(L7)负载均衡器

解析应用层协议——几乎总是 HTTP/HTTPS——并基于请求内容路由。

  • 例子: AWS ALB、nginx、HTTP 模式的 HAProxy、Envoy、Traefik、Caddy、Kubernetes Ingress controller。
  • 路由能力: 路径前缀、Host header、cookie、header、查询字符串、地理 IP、user agent。
  • 其他 L7 特性: TLS 终止、HTTP/2 → HTTP/1.1 降级后端通信、请求/响应 header 操作、压缩、限流、WAF 集成、认证/OAuth 代理。

优点: 拥有完整请求上下文的路由决策。语义化健康检查(从 /healthz 返回 200)。可观测性——按请求的日志、按路径的分位延迟、按路由的错误率。A/B 测试和灰度发布极其简单。

缺点: 每个请求消耗更多 CPU(payload 解析、TLS)。延迟更高(个位数毫秒,而非亚毫秒)。对长连接(WebSocket、HTTP/2 流、gRPC)的 TCP 状态处理更复杂。

何时使用: 任何 HTTP 流量的默认选择,除非你遇到了强制使用 L4 的规模或协议限制。

负载均衡算法

与层级选择相独立的是挑选后端的算法。常见的有:

  • 轮询(Round robin)。 请求 N 发到后端 N mod K。简单、均匀,但当后端容量不同或请求成本差异大时就会出错。
  • 最少连接(Least connections)。 发送给当前正在处理的连接最少的后端。在请求时长差异大时(长轮询、流式)表现更好。
  • 最少响应时间(Least response time)。 最少连接与观察到的延迟的混合。在混合负载下更好。
  • 加权轮询/加权最少连接。 后端按容量分配权重。在异构机群中必要。
  • 一致性哈希。 请求键 → 稳定后端。对缓存亲和性系统(Memcached、CDN 源站)、会话亲和性和分片数据存储至关重要。后端增删不需要完全重新哈希。
  • 两个选择的幂次法(Power of two choices)。 随机挑两个后端,发给负载更轻的那个。对最大负载有紧的上界,不需要中心协调,且对陈旧负载信息鲁棒。在现代服务网格中广泛使用。
  • 随机。 均匀随机挑选。在高 RPS 下表现出人意料地好,且完全不依赖负载信息。

选择启发式:同质无状态用轮询;请求时长差异大用最少连接;任何带缓存或状态亲和性的用一致性哈希;大型动态机群用 power-of-two。

健康检查——LB 悄然出错的地方

健康检查是 LB 配置中最不光鲜的部分,也是最常见的事故源。

  • 被动 vs 主动: 主动检查定时打 /healthz;被动检查观察生产流量的错误率。只用主动会错过瞬态问题;只用被动响应太慢。两者结合是正确默认。
  • 检查依赖链。 一个不验证数据库连接就返回 200 的 healthz 端点,比没有健康检查更糟——它让坏实例理直气壮地留在轮询中。
  • 不要检查得太激进。 对 1000 个后端以 100ms 间隔做健康检查 = 10K req/s 的健康检查流量。在规模下,健康检查本身就能占后端负载的可观比例。
  • 区分 liveness 与 readiness。 Kubernetes 显式区分这两者;其他系统常常混为一谈。Liveness = "进程还活着吗?" Readiness = "它是否准备好服务流量?" 混为一谈会导致热身期间的重启循环。

会话粘性——大多数情况下是反模式

会话亲和性把给定客户端在"会话"期间路由到同一后端,通常通过 cookie 或源 IP 哈希。

  • 常见理由: 后端持有内存中的会话状态,故障切换时会丢失。
  • 真正的问题: 粘性会话意味着任何后端故障都会丢失该主机上每一个在飞用户,并且负载会随着会话在长运行主机上积累而变得不均。

正确修复通常是让后端无状态(会话状态放在 Redis 或 JWT 中)。短时粘性(多步骤结账流程)可以接受,但在会话规模上应用就是一个坏味道。

TLS 终止

在哪里终止 TLS 影响安全态势和成本:

  • 在 L7 LB 上终止(最常见): LB 解密,基于请求路由,然后再加密回后端或在私有网络上发送明文。最易运维;后端不需要证书。
  • L4 LB 透传: LB 看不到明文;后端处理 TLS。当 LB 不应看到解密内容(如端到端加密),或需要基于 SNI 做 L4 路由时必需。
  • 两次终止(边缘 + 内部): 边缘 LB 终止,内部 LB 再次终止。昂贵但有时为合规所需。

对多数 Web 服务,在 L7 LB 上终止一次,内部在受信网络上走明文即可。两次终止模式在多数情况下只是合规产物,并非真正的安全提升。

真实世界的技术栈

  • 小型 SaaS: AWS ALB → EC2/ECS。单层,仅 L7。
  • 中等规模 SaaS: AWS NLB → ALB → 后端。NLB 处理超高吞吐 TCP;ALB 做请求路由。
  • 超大规模消费级: BGP Anycast → 边缘 L4 LB → 区域 L7 LB → 服务网格(Envoy sidecar)。四层,因为每层解决不同问题。
  • Kubernetes: 云 LB(L4)→ Ingress controller(L7,nginx/Traefik/Envoy)→ service(kube-proxy,L4)→ pod。K8s 内有四层 LB 属正常。

参见

References:Want to keep learning more?