负载均衡策略:轮询/最少连接/一致性哈希
请求来了分给哪个后端?负载均衡器决定了分配策略。本文讲清 L4(网络层)vs L7(应用层)、常见算法(轮询/加权/最少连接/IP哈希/一致性哈希)各自适用、健康检查与会话保持、以及 Nginx/ALB/Envoy 等工具选型。
本文要点
- 主题聚焦:DevOps
- 适合人群:独立开发者、全栈工程师、AI 应用开发者
- 核心价值:快速理解背景、实践路径、常见误区和工程建议
负载均衡策略:轮询 / 最少连接 / 一致性哈希
一台 8 核 32G 的应用服务器,扛到 5000 QPS 就开始抖动,CPU 飙到 90%,GC 停顿拉长,P99 延迟从 50ms 飙到 800ms。你加内存、调 JVM 参数、优化 SQL,边际收益越来越小。这时候真正的问题不在单机性能,而在单机这个模型本身已经到头了。
解法很朴素:加机器。三台各扛 2000 QPS,比一台硬扛 6000 QPS 稳得多,还顺带拿到了容错——挂一台还有两台顶。
但加完机器立刻冒出新问题:用户的请求来了,分给哪一台? 这就是负载均衡器(Load Balancer,LB)要解决的事。它不只是"均匀分配"那么简单,选错策略,会出现有的机器被打爆、有的机器在摸鱼;缓存命中率雪崩;用户登录态丢失;扩容瞬间流量迁移把集群拖垮。这篇把负载均衡的关键决策点一次讲透。
一、为什么必须有负载均衡
负载均衡器在客户端和后端服务之间加了一层,它做三件事:
- 分发:把请求按某种策略分到多个后端实例,水平扩展能力。
- 健康:持续探活后端,挂掉的自动摘除,用户无感。
- 收敛:对外只暴露一个入口(VIP / 域名),后端扩缩容对客户端透明。
没有 LB 的"多机"只是几台孤立的机器;有 LB 才是真正可运维的集群。几乎所有现代架构,从一台 Nginx 到全球级 GSLB,都建立在这套抽象上。
二、L4 vs L7:在 TCP 那一层切开,还是在 HTTP 那一层切开
负载均衡器按工作层级分两大类,这是最根本的选型决策。
L4(传输层负载均衡):只看 IP + 端口,把 TCP 连接转发出去,不看也不解 HTTP 内容。代表:HAProxy 的 tcp 模式、LVS、云厂商的 NLB、IPVS。
- 优点:极快,几乎只是改包头做转发,吞吐可以做到几十万 PPS;协议无关,MySQL、Redis、gRPC 都能转。
- 缺点:看不到 URL、Header、Cookie,没法按路径路由,也没法做基于 HTTP 的灰度。
L7(应用层负载均衡):终结 TCP,把 HTTP 请求读出来,基于 URL、Header、Cookie、甚至请求体做路由决策。代表:Nginx、HAProxy 的 http 模式、云厂商的 ALB、Envoy。
- 优点:灵活——
/api/*走 Java 集群,/static/*走 CDN 回源,带X-Canary: true的请求灰度到新版本;能改写请求、压缩、限流、WAF。 - 缺点:重。每个连接要解析 HTTP,延迟和 CPU 开销都高于 L4;协议绑死(基本就是 HTTP/HTTPS)。
实战经验:入口层用 L7(要 HTTPS 卸载、要路径路由、要灰度),内层服务间通信用 L4(要吞吐、要协议无关)。这是绝大多数中大型系统的默认分层。
三、五大算法:请求到底分给谁
这是 LB 最核心的策略。选对算法,集群利用率均匀、延迟低;选错,要么打爆单机,要么缓存全废。
1. 轮询(Round Robin):A→B→C→A→B→C,依次分配。简单、长期看均匀,适合所有后端机器配置相同、每个请求处理成本接近的场景。坑:如果请求成本差异大(有的查缓存 1ms,有的跑大 SQL 500ms),轮询会让某些机器偶然堆一堆慢请求。
2. 加权轮询(Weighted Round Robin):给每台机器一个权重,按权重比例分配。8 核机器权重 2,4 核机器权重 1,于是 8 核的分到 2 倍请求。适合机器配置不一致(混合机型、新老并存),也用来做灰度——新版本权重从 1 慢慢调到 100。Nginx 的默认算法就是平滑加权轮询(Smooth WRR),让权重分配在时间上更均匀,不会突发。
3. 最少连接(Least Connections):把请求分给当前活跃连接数最少的那台。这是最"智能"的简单算法,天然应对请求成本不均:慢请求多的机器连接数高,新请求就自动绕开它。适合请求处理时间差异大(API 网关、长连接、WebSocket、文件上传)。代价:LB 要维护每台后端的连接计数,有状态,但对现代 LB 不是问题。HAProxy 的默认就是 leastconn。
4. IP 哈希(IP Hash):对客户端 IP 做哈希,映射到固定后端。同一个客户端总是落到同一台机器。这本质是为了会话保持(见第五节)——session 存在本地内存里时,必须让同一用户回同一台。坑:哈希分布不一定均匀(某些大 NAT 出口的 IP 流量巨大,会打爆一台);加减节点时,几乎所有用户的映射都变了,session 全失效。
5. 一致性哈希(Consistent Hash):把后端和请求都映射到一个环上,请求顺时针找最近的节点。关键性质:加减节点时只有该节点相邻区间的请求会迁移,其他不变。迁移量 ≈ 1/N(N 为节点数),远好于普通哈希的几乎全迁。适合缓存层(Redis Cluster、Memcached、CDN 回源),因为缓存失效等于穿透打到 DB。常配合"虚拟节点"让分布更均匀。
算法对比一览:
| 算法 | 核心逻辑 | 最适合 | 主要坑 |
|---|---|---|---|
| 轮询 | 依次分 | 同质机器、等成本请求 | 慢请求堆积不均 |
| 加权轮询 | 按权重分 | 异构机型、灰度发布 | 权重需手动维护 |
| 最少连接 | 分给连接最少的 | 请求耗时差异大 | LB 需维护连接状态 |
| IP 哈希 | 同 IP 固定后端 | 本地 session 保持 | 分布不均、扩容全乱 |
| 一致性哈希 | 环上就近 | 缓存层、扩缩容频繁 | 需虚拟节点均衡 |
一个实操判断:默认拿「最少连接」起步,它对真实流量的适应性最好。只有当你有明确的会话保持或缓存亲和需求时,才上 IP 哈希 / 一致性哈希。
四、健康检查:挂了要能自动摘掉
LB 的另一项核心职责是只把流量分给健康的后端。没有健康检查,挂掉的机器会持续收到请求,LB 把超时当正常分发,用户看到的就是"半个集群故障"。
两种模式:
被动健康检查:LB 不主动探测,只看真实请求的结果。连续 N 次失败就标记该后端为 down,暂停分发;过一段时间再试。优点:零额外流量;缺点:用户请求是探针,恢复时第一批请求会失败。HAProxy、Nginx 默认支持。
主动健康检查:LB 定期向后端发探针(HTTP GET /health 或 TCP connect),根据响应决定健康状态。能更早发现故障,且不依赖真实流量。Nginx 开源版只有简化版,Nginx Plus / HAProxy / ALB / Envoy 都有完整主动探活。代价:后端要提供一个轻量、无副作用的健康端点,且别让探针本身成为负担(别在 /health 里查 DB)。
生产实践:两者都用——被动检查负责秒级失败隔离,主动检查负责周期性确认和恢复。健康端点要分层:liveness(进程活着)和 readiness(依赖就绪,能接流量)分开,DB 没连上时 readiness 应返回 503,让 LB 暂时摘除。
五、会话保持:同一用户回同一台
很多服务还是有状态的:session 在本地内存、本地文件缓存、WebSocket 长连接绑在某个进程。这些场景要求同一用户(或同一连接)始终落到同一台后端,这就是 Sticky Session(会话保持)。
实现方式:
- IP 哈希:上面讲过,粗粒度,问题是大 NAT 出口背后成千上万用户共用一个 IP,会被压到一台。
- Cookie 粘性:LB 在首次响应里种一个 Cookie(如
SERVERID=node3),后续请求带这个 Cookie 就回 node3。HAProxy、Nginx、ALB 都支持,粒度细,是主流方案。 - Header / 业务字段:按用户 ID、租户 ID 做一致性哈希,适合多租户 SaaS。
代价明确:后端挂了,粘在那台的用户的 session 会丢(除非 session 存外部 Redis)。所以会话保持是"必要的恶"——更优的架构是把状态外移(Redis、JWT),让后端无状态,任意机器都能接任意请求,这才是真正自由的水平扩展。能用无状态就别用 Sticky。
六、工具选型:Nginx / ALB / HAProxy / Envoy
| 工具 | 定位 | 典型场景 |
|---|---|---|
| Nginx | 通用 L7(也支持 L4 stream),生态最广 | 入口反代、HTTPS 卸载、静态资源、中小集群 LB。绝大多数团队的默认选择 |
| AWS ALB / 云厂商 L7 LB | 托管 L7,深度集成 VPC、IAM、证书、WAF | 云原生架构、不想自己运维 LB 实例、按 LCU 付费 |
| HAProxy | 高性能 L4/L7,配置精细,连接管理强 | 性能敏感的入口、L4 TCP 负载、数据库前置代理 |
| Envoy | 现代 L7,Sidecar / 服务网格核心 | 微服务网格(Istio 基于它)、gRPC、可观测性要求高 |
选型思路:云上要省心→ ALB;要控制力 + 性价比→ Nginx 自建;追求极致性能 / L4→ HAProxy;微服务网格化→ Envoy。大公司内部往往是多层叠加,不二选一。
七、多层均衡:大型架构是"洋葱"
真正高可用的系统,负载均衡不止一层,而是从外到内层层收敛:
- 全局层(GSLB / DNS):按地理、按运营商把
www.example.com解析到最近的区域。用户在北京就回北京机房的 VIP,在东京就回东京的。Anycast / GeoDNS 实现。 - 区域层(L7 ALB / Nginx):机房入口,做 HTTPS 卸载、按 URL 路由到不同服务(
/order走订单集群,/search走搜索集群)、按 Header 灰度。 - 服务层(L4 / Sidecar):服务内部实例间,按连接或请求分发,通常 L4 走 NLB/IPVS,或 Service Mesh 的 Envoy Sidecar 做精细 L7。
三层各司其职:全局扛地域级故障(某机房整体宕机),区域做业务路由和灰度,服务层做实例级负载均衡和健康隔离。没有哪一层能独立解决所有问题——单层 L7 ALB 扛不住跨地域,单层 DNS 又做不了 HTTP 灰度。洋葱式的分层,才是大系统应对"任何一层都可能挂"的现实之道。
小结
- 负载均衡器解决三件事:分发流量、健康隔离、收敛入口。没有它,"多机"只是孤立机器。
- L4 看 IP/端口、快但笨;L7 看 HTTP、灵活但重。入口用 L7,内层用 L4,是绝大多数系统的默认分层。
- 算法默认选最少连接,它对真实流量(请求耗时不均)适应性最好;缓存层才用一致性哈希,会话保持才用 IP 哈希 / Cookie。
- 健康检查被动 + 主动双保险,健康端点 liveness 与 readiness 分开,别在探针里查 DB。
- 会话保持是必要的恶,最优解是把状态外移让后端无状态;大型系统走多层均衡(GSLB → L7 → L4)层层收敛。
金句:负载均衡不是选一个最强的算法,而是让每一层只做自己擅长的事——入口懂业务,内层懂吞吐,全局懂地理。 把决策分到对的层,比在任何一层追求完美都重要。
Related
相关推荐
文章
Docker 部署 Next.js 社区项目的注意事项
围绕 DevOps、Next.js 的技术趋势、工程实践和独立开发视角。
文章
OpenTelemetry 如何帮助你观察 AI 应用?
围绕 DevOps、AI 的技术趋势、工程实践和独立开发视角。
文章
长运行工具调用:Operation、Poll 与 Cancel 的终态协议
初始请求超时后,工具可能仍在服务端运行;发出取消也不等于副作用已停止。本文为单次长运行工具调用建立 operation 身份、轮询水位、取消竞态和唯一终态接受点。
文章
Agent 记忆删除传播:从用户遗忘请求到可验证清除
删除主表一行不会清掉向量、摘要、缓存和恢复后的备份。本文用删除状态机、代际墓碑、派生血缘与端到端 canary 验证真正遗忘。