负载均衡器架构图:四层与七层、健康检查和算法

用图讲清负载均衡:四层与七层、两层架构、从轮询到最少请求的均衡算法、健康检查、连接排空和会话保持。

· 创始人兼开发者

发布于

负载均衡器架构图:四层与七层、健康检查和算法

“在前面放一个负载均衡器”是系统设计里最常听到的一句话,而它背后藏着几个在生产环境中至关重要的决定:它工作在哪一层、如何选择后端、如何判断后端是否健康,以及摘除一个后端时,正在处理的请求会怎样。

本文逐一讲解这些决定,并画出大多数大型部署最终都会采用的两层架构:在一组七层代理前面放一个四层负载均衡器。

四层与七层

四层 七层
能看到 IP 地址、端口、TCP 或 UDP 连接 HTTP 请求:主机、路径、请求头、Cookie
均衡的对象 连接 请求
TLS 透传,或者终止 通常会终止
可以按什么路由 端口、源地址 路径、主机、请求头、方法
还能做什么 极高吞吐、低延迟 重试、超时、改写、认证、限流
示例 云厂商的网络负载均衡器、IPVS、Maglev 式均衡器 Envoy、NGINX、HTTP 模式下的 HAProxy、云厂商的应用负载均衡器

最容易踩坑的区别是:四层均衡器均衡的是连接,而不是请求。使用 HTTP/2 或 gRPC 时,一个客户端只打开一条长连接,并在上面发送成千上万个请求,而这些请求全都会落到同一个后端。要把 gRPC 均衡好,需要七层代理或客户端负载均衡。

为什么要两层

大型架构通常把两者叠起来。四层负责持有公网 IP 地址、吸收连接洪峰,并按每条流的哈希把连接分发给一组七层代理。代理终止 TLS、应用路由规则,并把每个请求均衡到各个后端池。

每一层都可以独立扩容:四层按数据包和连接扩容,代理集群按请求量和 TLS 握手扩容。分层还能限制影响范围:一次错误的代理配置变更,影响的是某一层后面的代理,而不是用户连接的地址。而且由于代理是无状态的、共享同一份配置,增加一台代理就只是增加容量。

均衡算法

算法 如何选择 适用场景 注意事项
轮询 依次选择下一个后端 后端相同、请求相似 慢请求会在慢后端上堆积
加权轮询 按权重比例轮询 实例规格混用、逐步切换 权重是静态的
最少连接 打开连接数最少的后端 长连接 需要均衡器维护全局状态
最少请求(二选一) 随机挑两个,发给较空闲的那个 大多数 HTTP 服务 需要每个后端的请求数
一致性哈希 按某个键(如用户 ID)的哈希 缓存、会话亲和 热点键会导致负载不均

随机二选一的最少请求算法,是 HTTP 服务的一个好默认值。Envoy 的负载均衡器文档 是这样描述的:随机挑选两台主机(默认是两台),把请求发给活跃请求较少的那一台。Mitzenmacher 等人的研究表明,这几乎和检查所有后端一样好,却没有那样的开销。一致性哈希(如 ring hash 或 Maglev 算法)让同一个键始终落到同一个后端,后端变化时只移动一小部分键,这正是缓存所需要的。

健康检查

均衡器必须停止向无法提供服务的后端发送流量。两种机制配合使用:

  • 主动检查: 均衡器调用健康检查端点,比如每 5 秒请求一次 GET /healthz,连续失败达到一定次数后摘除该后端。
  • 被动检查(异常检测): 均衡器观察真实的响应,把返回错误或超时的后端踢出去,冷却一段时间后再放回来。

健康检查端点要诚实,但也要轻量。如果它检查每一个依赖,只要一个数据库变慢,所有后端就会同时变成不健康,均衡器就无处可发流量了。只检查这个实例处理请求所需的东西,把共享依赖留给应用自身的错误处理。

连接排空与会话保持

当某个后端因为部署或缩容而被摘除时,它应该停止接收新请求,但要把正在处理的请求处理完。配置一个比最慢的正常请求还要长的排空时间(云负载均衡器称之为注销延迟),并让应用在关闭时不再接收新的工作。

会话保持(sticky session)通常借助 Cookie,让同一个用户每次都被发往同一个后端。当状态保存在服务器内存里时它有用,但对其他一切都有害:负载不均、后端宕机时会话丢失、部署更困难。把会话状态移到共享存储里,能关掉会话保持的地方就关掉。

跨越单个区域

上面的一切都发生在单个站点之内。把用户分散到多个区域是另一层:基于 DNS 的路由,返回最近的健康区域(按延迟或地理位置路由);或者 anycast,即多个站点宣告同一个 IP 地址,由互联网路由把每个用户送到附近的站点。全局负载均衡选择区域,本文的两层选择服务器。要让健康信号向上传递,这样当某个区域的代理连不上后端池时,它会停止接收用户,而不是向用户返回错误。

架构图

客户端解析同一个 DNS 名称,通过 TCP 443 连接到四层负载均衡器,后者按流哈希把连接分发给同一代理集群中配置相同的两个七层代理。代理把 /api/orders 路由到 orders 池,把 /api/search 路由到 search 池,把 /static 路由到存储桶。

10 个组件 · 7 个连接

在 Archboard 中打开
图表内容
  • Clients
  • DNS: one name
  • Layer 4 load balancer
  • Layer 7 proxy 1
  • Layer 7 proxy 2
  • Orders pool: 6 instances
  • Search pool: 4 instances
  • Static files bucket
  • Proxy fleet, same config
  • resolve
  • TCP 443
  • flow hash
  • /api/orders
  • /api/search
  • /static
两层负载均衡。四层负载均衡器把 TCP 连接分发到一组七层代理上;代理终止 TLS,并按路径把每个 HTTP 请求路由到相应的后端池,池内使用最少请求算法和健康检查。

这张图的读法如下:

  • 左侧: 客户端解析同一个 DNS 名称,并在 443 端口与四层负载均衡器建立 TCP 连接。
  • 中间: 四层按流哈希把连接分发到代理集群。框架表示每台代理都运行同一份配置。
  • 右侧: 代理终止 TLS,并按路径路由每个请求:/api/orders 和 /api/search 发往各自的池,/static 发往存储桶。
  • Markdown 块 写明了路由表、每个池使用的算法和健康检查设置,这些细节平时通常只存在于配置文件里。

负载均衡器图上应该标注什么

  • 每一跳的层级和协议: TCP 443、HTTP/2、gRPC。
  • TLS 在哪里终止。 它决定了每一层能看到什么,以及证书放在哪里。
  • 每个池使用的算法, 如果用哈希,还要写出哈希键。
  • 健康检查的路径、间隔和阈值。
  • 连接排空和会话保持的设置, 如果有的话。

API 网关架构图 介绍了建立在路由之上的七层功能,Kubernetes Ingress 图 展示了集群内部的同样思路,网络拓扑图 则把负载均衡器放到更大的网络中来看。

在 Archboard 中构建

  1. 使用 Google 或 GitHub 登录,并新建一张图。
  2. 添加 C4 模型节点包中的一个 人员 表示客户端,再添加网络节点包中的一个 DNS 和一个 负载均衡器。
  3. 添加基础节点包中的一个 框架 表示代理集群,并在其中放入两个 容器 节点。
  4. 添加云节点包中的两个 计算实例 节点和一个 对象存储 节点,分别表示后端池和存储桶。
  5. 在连线上标注协议和路由,再添加一个基础节点包中的 Markdown 块,写明路由表和健康检查。

这个示例共用 10 个图表节点,在 Free 套餐的限制之内。

常见问题

四层负载均衡器和七层负载均衡器有什么区别? 四层均衡器分发 TCP 或 UDP 连接,不查看连接内部的内容。七层均衡器理解 HTTP,因此可以按路径或主机路由,并对单个请求进行均衡。

应该使用哪种负载均衡算法? 随机二选一的最少请求算法是 HTTP 的一个可靠默认值。当同一个键需要落到同一个后端时(比如缓存),使用一致性哈希。

为什么我的 gRPC 服务把所有流量都发到了一个 Pod? 因为 gRPC 使用长期存在的 HTTP/2 连接,而四层均衡器均衡的是连接,不是请求。在中间加一个七层代理,或者使用客户端负载均衡。

健康检查端点应该检查什么? 检查这个实例能否处理请求:进程正在运行,并且它自己的关键资源可用。避免检查那些会让所有后端同时失败的共享依赖。

会话保持是个好主意吗? 只有在状态无法离开服务器时才是。它会导致负载不均,并在故障时丢失会话;共享的会话存储通常更好。

免费开始 — 1 个席位,无需信用卡。

相关文章

← 返回博客

一分钟打开画布

使用 Google 或 GitHub 登录。单席位免费,团队加入后再升级。