负载均衡原理及算法详解
负载均衡把请求分摊到多个节点,换来水平扩展能力和故障容错。本文从四层与七层的本质差异讲起,梳理主流算法族谱、客户端与服务端两种落地形态,最后落到健康检查、会话保持与灰度发布的工程配合。
它解决什么问题
单机的 CPU、内存、连接数都有硬上限,单机宕机也意味着整体不可用。负载均衡回答的问题是:水平扩展出一批等价节点之后,流量怎么合理铺上去,节点异常时又怎么把它摘出去。
但先要划清边界:负载均衡只负责”分发”,不负责”提速”。后端每个实例都慢(慢 SQL、线程池耗尽、下游超时),负载均衡只会把慢请求摊得更均匀。判断瓶颈在哪一层,永远先于选择哪种负载均衡。
核心原理
四层与七层:转发依据的本质差异
四层负载均衡(LVS、F5)工作在传输层,只看到连接的源 IP、目的 IP 和端口,不解析报文内容。它以连接为单位做决策:TCP 连接建立时选定一台后端,这条连接上的后续报文全部发往同一台。因为不解包,转发可以在内核态完成——LVS 直接在内核里改包转发,单机扛住百万级并发连接并不稀奇。硬件形态的 F5 性能强、稳定,但价格昂贵,绝大多数公司用软件方案足够。
七层负载均衡(Nginx、Envoy)工作在应用层,必须先终止 TCP(通常还要终止 TLS),把 HTTP 报文完整解析出来,再基于 URL、Header、Cookie 等内容做路由。代价是每个请求都要经过用户态代理,性能开销更高;换来的是内容感知能力——按路径分流、按 Header 灰度、做缓存压缩和 TLS 卸载。
一句话概括:四层按”连接”分发,快但看不见内容;七层按”请求”分发,看得见内容但要先完整接收。随着硬件性能提升,两者的性能差距在多数业务场景已不再关键,选型主要看是否需要内容路由。
客户端与服务端负载均衡
服务端负载均衡在请求路径上放一个独立中间节点(Nginx、LVS、F5),调用方只看到一个虚拟地址。治理集中、对调用方透明,代价是中间节点自身要做高可用,且多一跳网络开销。
客户端负载均衡把选节点逻辑放进调用方进程:客户端从注册中心拉取服务实例列表,本地执行负载均衡算法后直连目标。Dubbo 默认自带,Spring Cloud 体系里是 Ribbon(Netflix,已停止新特性开发)和官方的 Spring Cloud LoadBalancer。好处是省一跳、没有中心节点瓶颈;代价是逻辑绑定语言栈、策略升级跟随应用发布,且实例列表从注册中心同步下来存在延迟。
深入机制
算法族谱
- 轮询(Round Robin):依次分配,简单公平,但不感知后端负载差异。加权轮询给性能强的机器更多配额;平滑加权轮询(Nginx 最早实现,Dubbo 借鉴优化)避免高权重机器被连续集中命中,把流量在时间轴上摊开。
- 随机:实现最简,长期统计上与轮询等价,但短时间窗口可能偏斜。”两次随机”(Power of Two Choices)随机挑两台再选负载低的那台,以极低代价显著改善尾部偏斜,是随机法的高性价比增强。
- 最少连接 / 最少活跃:选当前在途请求最少的节点,隐含”连接多即负载高”的假设,适合请求耗时不均的长连接场景。但连接数不等于真实负载,慢请求堆积时仍会误判。
- 一致性哈希:把节点和请求 key 都映射到同一个哈希环,key 顺时针落到第一个节点。节点增减只影响环上相邻区间,而不是像
hash % N那样几乎全部重排——这是它对缓存类场景的核心价值。为防止物理节点在环上分布不均,通常给每个节点映射上百个虚拟节点。 - 自适应 / 最快响应:按实时响应时间和错误率动态调整权重,能把流量从变慢的节点引开。但它依赖指标新鲜度,指标滞后时会引发流量震荡,需要配合熔断使用。
健康检查比算法更重要
算法决定正常时怎么分流,健康检查决定异常时摘不摘节点。要区分两类检查:存活检查(端口通不通)与就绪检查(/health 返回是否正常、依赖的数据库和缓存是否可用)。只查端口,会把”活着但已病”的节点留在池子里。节点恢复后也不该立刻打满流量,渐进放量(慢启动)给 JIT 编译、连接池、本地缓存留出预热时间,否则恢复瞬间就是一次抖动。
工程实践
- 典型入口分层:公网入口用 LVS + Keepalived 做四层转发与高可用,后面接 Nginx 或网关做七层路由,服务内部再用客户端负载均衡。中小规模系统只用 Nginx 一层通常就够。
- 会话保持:
ip_hash或 Cookie 粘连能把同一用户固定到同一节点,但破坏均衡,扩缩容时用户被批量迁移。更推荐把会话外置到 Redis,让节点彻底无状态;必须粘连时,先评估节点故障对在线用户的影响面。 - 灰度发布:七层负载均衡是灰度的天然抓手——按 Header、Cookie、用户 ID 尾号把特定流量路由到灰度实例组,或用权重逐步放量。灰度规则必须能一键关闭,发布配置与回滚路径要提前演练。
- 上线前检查:实例列表变更同步是否及时、健康检查间隔与失败阈值是否合理、摘节点时存量请求是否优雅排空。
对比与权衡
| 维度 | 四层(LVS/F5) | 七层(Nginx/Envoy) | 客户端(Ribbon/LoadBalancer) |
|---|---|---|---|
| 转发依据 | IP + 端口,连接级 | URL/Header/Cookie,请求级 | 注册中心实例列表,请求级 |
| 性能开销 | 极低,可内核态转发 | 较高,需终止连接解析报文 | 无额外网络跳数 |
| 灵活性 | 弱 | 强,内容路由、灰度、缓存 | 强,可编程定制策略 |
| 高可用 | 依赖 Keepalived 主备 | 自身需集群化 | 依赖注册中心推送 |
| 典型位置 | 机房流量入口 | 网关、反向代理 | 服务间内部调用 |
算法选择经验:无状态短请求用轮询或随机即可;请求耗时差异大用最少连接;需要会话或缓存亲和用一致性哈希;机器异构叠加权重。
面试视角
四层和七层负载均衡的本质区别是什么? 四层以连接为单位,只看 IP 和端口就转发,不解析报文,可在内核态完成,性能极高;七层必须先终止 TCP/TLS、解析出 HTTP 内容,才能按 URL、Header 路由。代价是性能开销,收益是内容感知。
为什么一致性哈希比 hash 取模更适合缓存? hash % N 在 N 变化时几乎所有 key 的映射都会改变,缓存大面积失效;一致性哈希把节点和 key 放进同一哈希环,增删节点只影响环上相邻区间的 key,迁移量约为 1/N。虚拟节点用来解决节点较少时环上分布不均的问题。
客户端和服务端负载均衡怎么选? 服务间调用选客户端——省一跳、无中心瓶颈,Dubbo、Spring Cloud LoadBalancer 是代表;对外入口选服务端——对调用方透明、治理集中,Nginx 是代表。客户端方案的软肋是语言绑定和实例列表同步延迟。
节点变慢,负载均衡能自动处理吗? 只能部分处理:最少连接、自适应策略会减少向慢节点派发,但根治要靠健康检查把病态节点摘除,配合熔断与限流。指标滞后的窗口期内仍有请求打到慢节点,这就是为什么监控要看 P99 而不只是平均值。
小结
- 负载均衡的价值是水平扩展与故障摘除,不是让慢系统变快;先定位瓶颈,再谈分发。
- 四层按连接、七层按内容;四层快、七层活,常见组合是”LVS 扛入口流量 + Nginx 做内容路由”。
- 算法没有银弹:轮询打底,权重适配异构机器,最少连接应对耗时差异,一致性哈希应对节点变动的亲和场景。
- 工程落地的关键不在算法而在配套:健康检查区分存活与就绪、节点恢复渐进放量、会话外置、灰度规则可秒级关闭。

