23、负载均衡可靠性:负载均衡算法、健康探测、会话保持与LVS/Nginx实践

各位好,今天我们来聊聊负载均衡的可靠性。说实话,很多团队把负载均衡想简单了——以为配个轮询就完事了。但我在生产环境里踩过的坑告诉我,负载均衡的可靠性,直接决定了整个集群的生死

你想想看,如果负载均衡器本身挂了,或者它把请求转发给一个已经宕机的后端,那后果是什么?用户直接报错,业务中断。所以,今天我把这块掰开揉碎了讲清楚。

负载均衡算法:选对路子,事半功倍

负载均衡算法,说白了就是决定「下一个请求该发给谁」。我见过不少项目,从头到尾只用轮询,结果后端机器配置不一样,有的累死,有的闲死。

轮询(Round Robin)

最简单的算法。请求按顺序轮流发给每个后端。适合后端性能均等的场景。但我在项目中遇到过,某台机器因为磁盘 I/O 高,响应变慢,轮询还是照样给它发请求,结果拖慢了整个链路。

加权轮询(Weighted Round Robin)

给每台后端配一个权重。性能好的机器权重高,多分点请求。这个我比较常用。比如 8 核 16G 的机器权重设 3,4 核 8G 的设 1。注意,权重不是越高越好,我曾经设过 10:1,结果高权重那台 CPU 直接飙到 90%。

最小连接(Least Connections)

把请求发给当前活跃连接数最少的后端。这个算法对长连接服务特别友好。比如 WebSocket 或数据库连接池。嗯,这里要注意:如果后端处理请求的速度差异大,最小连接反而可能不准。

算法适用场景我踩过的坑
轮询后端性能均等某台机器慢,拖累整体
加权轮询后端性能不均权重差距太大,高权重机器过载
最小连接长连接服务处理速度差异大时不准

健康探测:别把请求发给「死人」

负载均衡器怎么知道后端还活着?靠健康探测。我见过最惨的一次,后端应用已经 OOM 了,但进程还在,端口还开着。负载均衡器探测端口返回成功,继续往里发请求,结果全部超时。

所以,健康探测一定要做「业务级」的。不要只检查端口,要检查一个专门的健康检查接口,比如 /health,这个接口内部会验证数据库连接、缓存连接等关键依赖。

核心原则:健康探测要模拟真实请求,而不是只检查进程是否活着。

我曾经在 Nginx 里配过这样的健康检查:

upstream backend {
    server 192.168.1.10:8080 max_fails=3 fail_timeout=30s;
    server 192.168.1.11:8080 max_fails=3 fail_timeout=30s;
    
    # 主动健康检查(需要 nginx-plus 或第三方模块)
    health_check interval=5s fails=3 passes=2 uri=/health;
}

这里 max_fails=3 的意思是,连续失败 3 次就认为后端挂了,然后 fail_timeout=30s 内不再转发请求。30 秒后再试一次。这个机制救过我好几次。

会话保持:别让用户「迷路」

会话保持,也叫粘性会话。说白了就是:同一个用户的请求,尽量发给同一台后端。为什么?因为用户登录状态、购物车数据可能就存在那台机器的内存里。

我早期做电商项目时,没配会话保持。用户登录后刷新一下页面,又跳到另一台机器上,结果提示「请重新登录」。用户直接骂娘。

常见的会话保持方式有几种:

  • 源地址哈希:根据客户端 IP 计算哈希,固定分配到某台后端。简单,但如果有大量用户来自同一个出口 IP(比如公司内网),就会全部挤到一台机器上。
  • Cookie 插入:负载均衡器在响应里插入一个 Cookie,后续请求带上这个 Cookie 就能路由到同一台后端。这个更灵活,我比较推荐。
  • Redis 集中会话:把会话数据存到 Redis 里,所有后端共享。这样就不需要会话保持了,但架构复杂一点。

我的建议:如果业务允许,尽量用 Redis 集中会话。这样后端可以随意扩缩容,不用操心会话保持的问题。

LVS 与 Nginx 实践:四层与七层的配合

LVS 工作在四层(传输层),Nginx 工作在七层(应用层)。我一般把 LVS 放在最前面做流量入口,后面挂 Nginx 做反向代理和负载均衡。这样 LVS 扛高并发,Nginx 做精细路由。

下面这张图是我常用的架构:

用户请求 LVS(四层) Nginx 1(七层) Nginx 2(七层) Nginx 3(七层) App 1 App 2 App 3 App 4 用户 → LVS(四层分发) → Nginx(七层负载) → 后端应用

LVS 我用的是 DR 模式(直接路由)。配置起来不复杂,但要注意:LVS 本身不能做健康检查,需要配合 keepalived 来实现。keepalived 会定期检查后端 RS 的健康状态,如果发现某台挂了,就自动从 LVS 的转发列表里摘掉。

Nginx 这边,我一般用 upstream 模块做负载均衡。下面是一个生产级的配置示例:

upstream backend {
    least_conn;
    server 192.168.1.10:8080 weight=3 max_fails=2 fail_timeout=10s;
    server 192.168.1.11:8080 weight=2 max_fails=2 fail_timeout=10s;
    server 192.168.1.12:8080 backup;  # 备用服务器
}

server {
    listen 80;
    
    location / {
        proxy_pass http://backend;
        proxy_set_header Host $host;
        proxy_set_header X-Real-IP $remote_addr;
        
        # 会话保持:基于 Cookie
        proxy_cookie_path / "/; HttpOnly; SameSite=lax";
    }
}

注意backup 标记的服务器只在其他所有服务器都不可用时才启用。我曾经因为忘记配 backup,导致所有后端都挂了之后,Nginx 直接返回 502。后来我习惯至少配一台 backup 机器,哪怕配置低一点,至少能兜底。

避坑指南

最后,分享几个我亲身踩过的坑:

  • 健康检查间隔太短:我曾经设成 1 秒检查一次,结果后端压力大时,健康检查请求反而成了压垮骆驼的最后一根稻草。建议 5 秒以上。
  • 会话保持和负载均衡的矛盾:用了会话保持,某台后端挂了,上面的用户会话就丢了。所以,要么用集中会话,要么做好会话迁移。
  • LVS 的 ARP 问题:DR 模式下,如果后端 RS 的 lo 接口配置了 VIP,但没有抑制 ARP 响应,会导致网络混乱。这个坑我调了整整一天。

好了,负载均衡的可靠性就聊到这里。记住一句话:没有万能的算法,只有最适合业务的配置。动手试试,你会有更深的体会。


公众号:蓝海资料掘金营,微信deep3321