关于 load-balancing
面向系统设计者的负载均衡技能,帮助在后端池间分配请求并规划健康检查与故障处理。涵盖 L4、L7、分配算法和高可用拓扑,并强调避免恢复节点过载及均衡器成为单点故障。
主要功能
- 01选择 L4 或 L7 均衡
- 02比较请求分配算法
- 03规划主动-主动或主动-被动拓扑
- 04定义健康检查与连接排空
- 05处理慢启动和重试放大
适用场景
- 01将请求分发到多个后端
- 02为后端池设计健康门控
- 03规划均衡器高可用与故障切换
安装
复制下面的命令,在终端中运行。
npx skills add https:/ / github.com/ proyecto26/ system-design-skills --skill load-balancing安装后在 Claude Code 中输入/load-balancing确认已加载。
包含的文件
除 SKILL.md 外,安装时一起下载的参考资料和脚本。
references/deep-dive.mdreferences/providers/aws.mdazure.mdgcp.mdgeneric.md详细介绍
负载均衡
将传入请求分配到一组相同的后端,让任何一台服务器都不会成为瓶颈或单点故障。如果配置不当,负载均衡器本身就会成为它原本要消除的单点故障(SPOF),将流量路由到已宕机的服务器,或在某个后端已不堪重负时仍不断向其发送请求,加剧故障。
适用场景
当多个后端承担相同职责,且流量必须在它们之间分配时;当需要消除单一入口这一单点故障时;当需要增减服务器而不让客户端察觉时(这是实现无状态层和自动扩缩容的基础);或者需要在私有服务器集群前设置一个公共地址,并在同一处处理 TLS 终止、健康检查和路由时,都可以使用负载均衡器。
不适用场景
如果单个后端能够轻松应对峰值负载,就暂时不需要负载均衡器——增加它会引入新组件、新故障模式和新的运维对象(YAGNI)。如果真正的瓶颈是数据库或单个热点分片,那么在 Web 层前放置负载均衡器毫无帮助;应先诊断实际的限制因素(→ back-of-the-envelope)。跨区域流量调度通常通过边缘的 DNS/任播实现,而不是 L4/L7 负载均衡器(→ content-delivery)。针对客户端的请求限流属于 resilience-failure 负责的策略问题,而非负载均衡算法的问题。
先明确需求
- 协议和所需层级 — 需要原始 TCP/UDP 吞吐量(L4),还是需要根据路径/主机/标头/Cookie 进行 HTTP 感知路由(L7)?这决定负载均衡器类型。
- 状态 — 后端是无状态的,还是会话驻留在某台服务器上(因此必须启用会话亲和性)?将状态移出几乎总是更好的选择。
- 峰值 QPS 和连接数 — 是一条高吞吐连接流,还是大量短请求?这会影响算法和负载均衡器的容量规划(→
back-of-the-envelope)。 - 健康信号 — “健康”的定义是什么(接受 TCP 连接?
/healthz返回 200?检查深层依赖?),以及故障节点必须多快从池中移除? - TLS — 在负载均衡器处终止 TLS(减轻后端负担、检查 L7),还是端到端透传(合规要求/mTLS)?
选项
检查层级
- **L4(传输层):**根据 IP/端口进行路由;通过 NAT 或 DSR 转发数据包,不读取负载。适用于需要原始吞吐量、使用非 HTTP 协议或希望将额外延迟降至最低的场景。
- **L7(应用层):**终止连接,读取 HTTP(主机、路径、标头、Cookie),然后进行路由。适用于需要基于内容的路由、按路由划分后端池、TLS 终止或重写请求的场景。
分配算法
- **轮询 / 加权轮询:**均匀轮换,并按容量加权。适用于负载均匀的无状态后端。
- **最少连接 / 最短响应时间:**将请求发送到最空闲的节点。适用于请求开销差异较大的场景(长连接、混合工作负载)。
- **IP 哈希 / 一致性哈希:**将客户端(或键)映射到固定后端。适用于无需在服务器端存储会话状态的亲和性,或需要保持缓存局部性的场景。
- **随机(两次选择):**随机选取两个节点,并选择负载较轻的那个——成本低,在大规模场景下效果出奇地均匀。
拓扑结构
- **主备模式:**一个负载均衡器提供服务,发生故障时由备用节点接管 VIP。实现高可用性较简单。
- **主动-主动模式:**多个负载均衡器共同分担负载(通过 DNS 或任播)。消除负载均衡器自身的单点故障,并增加容量余量。
**反向代理的作用:**L7 负载均衡器同时也是反向代理——它是唯一的公共入口,隐藏后端,终止 TLS,压缩和缓存内容,并集中处理路由。即使只有一个后端,反向代理也能带来这些好处,因此仍值得使用;多后端场景下的负载均衡只是同一组件的另一种用途。
权衡
| 选项 | 解决的问题 | 带来的问题 | 何时更改 |
|---|---|---|---|
| L4 负载均衡 | 最大吞吐量、低延迟,支持任意协议 | 无法识别内容;不支持路径/标头路由,也不能检查 TLS | 需要基于内容路由或终止 TLS → L7 |
| L7 负载均衡 | 内容路由、TLS 卸载、重写、可观测性 | 延迟和 CPU 开销更高;会终止连接(状态更多) | 原始吞吐量占主导且路由简单 → L4 |
| 轮询 | 极其简单,适用于工作负载均匀的情况 | 忽略实际负载;慢节点仍会分到请求 | 请求开销差异很大 → 最少连接数 |
| 最少连接数 | 能适应请求开销不均 | 需要实时连接状态;可能让流量集中到刚恢复的节点 | 后端性能均一 → 轮询就足够 |
| IP/一致性哈希 | 无需会话存储即可实现亲和性和缓存局部性 | 分布不均;后端池变更时会重新平衡 | 可以将状态外置 → 轮询 + 共享存储 |
| 会话保持 | 目前可用于有状态后端 | 破坏均匀分布,使缩容更复杂,节点故障时会丢失会话 | 状态可以迁移到 Redis/DB → 取消亲和性 |
| 主动-被动 | 为负载均衡器提供简单的高可用 | 备用节点闲置;故障转移存在间隙(数秒) | 需要无间隙的容量余量 → 主动-主动 |
压力下的行为
负载均衡器位于所有请求的路径上,因此它的故障模式就是整个系统的故障模式。
- 健康检查导致流量骤增(典型陷阱): 后端恢复后,负载均衡器将其标记为健康,全部流量便涌向这个单一的冷启动节点——缓存为空、JIT 尚未预热、连接队列已满——导致它未能通过下一次健康检查并退出,反复振荡(状态抖动)。过于激进的检查实际上可能会 DDoS 正在恢复的服务。可通过慢启动 / 逐步增加连接数、设置宽松的失败阈值(驱逐前连续失败 N 次,重新接纳前连续成功 M 次)、检查抖动,以及在依赖路径上设置断路器来缓解(→
resilience-failure)。 - 重试放大: 负载均衡器(或客户端)会将失败的请求重试到其余健康节点,因此丢失一个节点可能引发级联故障,幸存节点不仅要承担该节点的负载,还要处理重试请求。限制重试次数;使用带抖动的退避策略(由
resilience-failure负责)。 - 负载不均 / 热点后端: 长连接或会话保持会使负载集中在少数节点上;轮询无法发现这一点。应监控每个后端的利用率,而不只是整体指标。
- 负载均衡器成为单点故障: 单个负载均衡器故障会导致一切停摆。采用主动-主动或主动-被动模式,并配置快速 VIP/任播故障转移。
- 重连风暴: 负载均衡器重启时,所有客户端会同时重连。应分阶段排空连接,并设置连接数限制。
监控: 每个后端的请求速率和延迟(p99)、健康主机数、健康检查通过/失败情况和状态抖动率、连接数、负载均衡器与后端的 5xx 错误率(两者差异有助于定位故障),以及活动连接分布。
如何应用
- 明确输入信息 — 回答 先澄清 中的问题:协议/层级、后端是否无状态、峰值 QPS 和连接数、“健康”的含义,以及 TLS 策略。这些信息决定之后的所有选择。
- 根据权衡表选择层级和算法 — 需要原始吞吐量或处理非 HTTP 流量时选择 L4;需要按内容路由或卸载 TLS 时选择 L7。无状态后端配置一致时,默认选择轮询;仅当请求开销有所不同才考虑最少连接;只有状态暂时无法迁移时才使用基于哈希的亲和性。
- 设置关键参数 — 明确编写健康检查配置(方法、路径、预期状态码、间隔、失败/恢复阈值)和连接池的排空延迟;启用慢启动,让恢复中的节点逐步接收流量,而不是一下子承受全部流量。
- 对方案进行压力测试 — 逐一检查故障模式:健康检查请求风暴、重试放大、后端过热,以及负载均衡器成为单点故障(SPOF)。采用主动-主动(或通过快速 VIP/任播故障转移实现主动-被动),避免负载均衡器成为新的单点故障。
- 用数字确定容量 — 确认负载均衡器能够处理峰值 QPS 和并发连接,并验证真正的容量瓶颈是后端集群,而非负载均衡器。从
back-of-the-envelope获取每台服务器的 QPS 和连接内存占用数据。 - 选择提供商 — 默认使用通用方案;如果指定了云服务商,请查阅其提供商文件,了解托管服务对应方案和限制。
- 在回答中说明权衡 — 提供一张
Decision | Solves | Worsens | Change it when…表格,为此处做出的每项选择各列一行(例如,所选方案、存储、故障策略);最后一列填写具体的触发条件(一个数值或可观察信号)。然后指出最先会出现的故障。
应做与不应做
应做
- 将会话状态存入共享存储,以便使用普通轮询和自动扩缩容。
- 明确规定健康检查,并有意识地选择浅层检查还是深层检查。
- 采用主动-主动或主动-被动模式运行负载均衡器,避免其成为 SPOF。
- 启用慢启动,并设置较宽松的失败/恢复阈值,防止状态抖动。
- 监控各后端的利用率,而不只是整体利用率,以发现过热节点。
不应做
- 单个后端已能处理峰值负载时,不要增加负载均衡器(YAGNI)——反向代理或许仍有价值,但无需在多个后端之间进行负载均衡。
- 不要让针对缓慢依赖项的深层健康检查一次性剔除整个集群。
- 状态可以直接迁出时,不要急于使用粘性会话。
- 不要盲目向剩余节点重试;应限制重试次数,并使用退避/抖动。
- 在确认后端集群有容量瓶颈之前,不要假定负载均衡器是瓶颈。
关键数据
现代软件负载均衡器(HAProxy/Nginx/Envoy)每个实例可处理每秒数万次请求和数千个并发连接,通常远高于单个 Web/应用节点的处理能力,因此很少会成为首要瓶颈。应根据峰值 QPS 和并发连接数确定其容量,并确认真正的容量瓶颈是后端集群,而非负载均衡器。每台服务器的 QPS、连接内存开销和峰值系数计算均见 back-of-the-envelope——此处不再重复,请从中提取数据,为连接池和负载均衡器确定容量。
接口示意
负载均衡本身没有独立的请求/响应契约,但有两项配置会对系统产生重大影响,值得明确记录:
- 健康检查: 方法 + 路径 + 预期状态码 + 间隔 + 阈值,例如
GET /healthz → 200, every 5s, unhealthy after 3 fails, healthy after 2 passes。应有意识地决定将/healthz设为浅层检查(进程存活)还是深层检查(检查 DB)——针对缓慢依赖项的深层检查一旦失败,就可能一次性剔除整个集群。 - 后端池/目标组:
host:port目标、权重,以及缩容时使用的排空/注销延迟,以便正在处理的请求能够完成。
负载均衡如何实现无状态层
只有在任何服务器都能处理任何请求时,才能将请求自由分发到可互换的服务器上——也就是说,服务器上不保存会话状态。将会话状态放入共享存储(Redis/DB),这样负载均衡器就能使用普通轮询,随时添加或移除节点,并让自动扩缩组增减服务器池。只有在状态暂时无法迁移时,才将会话粘滞作为备用方案,但这会牺牲分布均匀性和轻松缩容的能力。水平扩展和无状态层由 scaling-evolution 负责;本技能提供实现路由的机制。
选择提供商
默认使用上述通用方案。如果用户指定了云服务,请阅读
references/providers/<provider>.md,了解托管服务映射、配额/限制和提供商特定的权衡。如果该提供商没有对应文件,则采用通用方案。
图示
要将请求路径(客户端 → 负载均衡器 → 后端服务器池)或健康检查/故障转移流程可视化,请使用插件内的 architecture-diagram 技能。也可以用 client → [LB] → {web1, web2, web3} → shared state 快速绘制内联草图辅助推理;不要嵌入 Mermaid。
相关构建模块
scaling-evolution— 为本技能提供基础:它负责无状态层以及水平扩展与垂直扩展;本技能则提供路由机制,让该服务器集群可被访问。resilience-failure— 与本技能配合使用,适用于负载均衡器可能放大故障的情况;相关概念归此技能负责:重试/退避/抖动、断路器、故障转移和限流可用于解决健康检查风暴和重试风暴。content-delivery— 跨区域流量场景下的替代方案:当“均衡”实际上是将用户引导至最近的区域时,正确的工具是在边缘进行地理位置/DNS/任播路由(由该技能负责),而不是 L4/L7 负载均衡器。back-of-the-envelope— 依赖本技能提供的 QPS、连接数和峰值系数数据,用于确定服务器池和负载均衡器的规模。system-design— 当设计从一台服务器扩展到多台时,负责将请求路由到此处的协调器。
参考资料
references/deep-dive.md— L4 NAT 与 DSR、L7 终止的工作原理、算法内部细节(包括用于亲和性的 一致性哈希)、健康检查调优与慢启动、连接排空、TLS 终止与透传。配置负载均衡器时,请详细阅读。references/providers/{generic,aws,azure,gcp}.md— 各环境的服务映射、限制和注意事项。