负载均衡与反向代理:故障转移的三大支柱

做高可用,绕不开反向代理和负载均衡。

这三兄弟——Nginx、HAProxy、LVS——我几乎每天都在接触。不少人觉得它们功能重叠,非要选一个。我个人的习惯是:别急着站队,先搞清楚它们的定位。这章我们就掰开揉碎了聊聊。

核心观点:负载均衡不是轮询发请求那么简单。真正的价值在于健康检查和故障转移的实时性。一个挂了,另一个马上顶上。
负载均衡三层架构与故障转移核心流程 客户端请求 四层负载:LVS(IPVS) | F5 七层负载:Nginx | HAProxy 健康检查策略:主动探测 + 被动熔断 探测到故障 自动摘除节点 App Server 1 (存活) App Server 2 (故障) App Server 3 (存活) 可组合叠加

一、Nginx:七层代理里的多面手

Nginx 本质上是一个反向代理服务器。它能解析 HTTP 协议,所以能做的动作远不止转发请求。我之前有一个项目,客户端请求里带了各种奇怪的 header,后端服务认不出,全靠 Nginx 的 rewrite 模块做清洗。

它的工作分两步:

  1. 接收请求:Nginx 的 worker 进程监听端口,拿到完整 TCP 连接后解析 HTTP 报文。
  2. 选择后端:根据 URL、cookie 或请求头,从 upstream 块里挑一个 server。
upstream backend {
    server 10.0.1.2:8080 weight=3;
    server 10.0.1.3:8080 weight=2;
    server 10.0.1.4:8080 backup;
    keepalive 32;
}

server {
    listen 80;
    location /api/ {
        proxy_pass http://backend;
        proxy_next_upstream error timeout http_500;
    }
}
我的小技巧:proxy_next_upstream 这个指令是故障转移的关键。我习惯加上 http_500 和 http_502,因为很多故障是应用层抛出的。光靠超时判断太慢了。

Nginx 的健康检查。官方开源版只支持被动检查——响应超时或返回 5xx 就标记为不可用,下次请求自动跳过。如果你需要主动探查,推荐用 nginx_upstream_check_module 或者直接用商业版的 Plus。

有回我帮一家电商做压测,发现 Nginx 频繁把正常的 pod 标记为失效。查了半天,原因是后端慢查询导致响应超过 30 秒,触发了 proxy_next_upstream。这就是典型的被动检查缺陷——它只看结果,不问原因。

二、HAProxy:负载均衡的瑞士军刀

HAProxy 是专业的 TCP/HTTP 负载均衡器。你想想看,它的资源消耗能小到什么程度?我见过单台 HAProxy 扛下 50 万并发连接,CPU 占用还不到 30%。

健康检查是 HAProxy 的强项。它支持三种模式:

模式 检查方式 典型场景
option httpchk 发送 HTTP 请求,检查返回状态码 Web 应用健康探测
option tcp-check 打开 TCP 连接并发送期望字符串 Redis、MySQL 等自定义协议
option mysql-check 专用的 MySQL ping 检查 数据库读写分离
backend web_servers
    mode http
    balance roundrobin
    option httpchk GET /health HTTP/1.1\r\nHost:\ example.com
    server app1 10.0.1.5:80 check inter 3000 fall 3 rise 2
    server app2 10.0.1.6:80 check inter 3000 fall 3 rise 2

参数 inter 3000 fall 3 rise 2 的意思是:每 3 秒检查一次,连续失败 3 次才标记为不可用,连续成功 2 次才恢复。这样的好处很明显——避免瞬时的网络抖动导致误杀。我曾经就因为 fall 设成 1,每次发版重启期间 HAProxy 就把所有节点踢掉了,导致长达 2 分钟的 503。

避坑指南:我曾经遇到过一个 case,HAProxy 配置了 httpchk,但后端健康检查页面只返回 200 和空 body。应用内存泄漏,GC 已经跟不上请求流量,但 HTTP 响应还是 200。等我发现问题时,半数节点已经 OOM 了。所以健康检查页面一定要做全面的自检——不只是服务存活着,还要检查连接池状态、队列长度等。

三、LVS:Linux 内核级别的四层转发

LVS 和前面两个完全不同。它工作在 OSI 模型的第四层,运行在 Linux 内核空间。说白了,它就是一个高性能的 IPVS 模块。

LVS 有三种工作模式,我经常用一张表给团队对比:

模式 原理 优点 缺点
DR(直接路由) LVS 只改 MAC 地址,后端直接回包给客户端 性能最高,无回包瓶颈 后端需要配置 VIP 和 ARP 抑制
TUN(隧道) LVS 封装 IPIP 隧道发给后端 支持跨机房转发 隧道开销大,配置复杂
NAT LVS 做 DNAT,请求和回包都经过它 后端不用做特殊配置 回包走 LVS 成为瓶颈

大多数高并发场景我推荐 DR 模式。但要注意,后端服务器的回包不能经过 LVS,否则会形成环路。这个坑我踩过——配了个 RIP 的默认网关指向 LVS,结果所有回应包又回到了 LVS,形成死循环。改成 169.254.0.1 的直连路由才解决。

# 配置虚 IP
ip addr add 10.0.0.100/32 dev lo:0

# 添加集群服务
ipvsadm -A -t 10.0.0.100:80 -s rr

# 添加后端真实服务器(DR 模式)
ipvsadm -a -t 10.0.0.100:80 -r 10.0.1.10:80 -g
ipvsadm -a -t 10.0.0.100:80 -r 10.0.1.11:80 -g

至于健康检查,LVS 内核里自带了一个叫 keepalived 的伴侣程序。它负责做健康探测,如果某个 RS 挂了,它会通知内核的 IPVS 模块把它从调度列表中摘除。

策略对比总结:
  • Nginx:擅长七层路由,健康检查被动为主,故障转移依赖 proxy_next_upstream。
  • HAProxy:独有的精细控制,支持多协议自定义健康检查,适合复杂后端。
  • LVS+Keepalived:纯四层转发,性能极致,健康检查由 Keepalived 完成,适合入口层。

四、选型建议:得看你的场景

说了这么多,实战中怎么选?我个人的粗框架是这样:

  • 小规模集群(5-20 台):直接用 Nginx,七层能力强,能当网关还能做静态资源服务。健康检查够用就行。
  • 中大型服务(50-500 台):HAProxy 作为中间层,做精细的流量调度和健康检查。前面再挂一层 LVS 做入口分流。
  • 要求极致性能:LVS DR 模式做入口,HAProxy 做 API 网关。两者结合,既能抗流量又能做精细路由。
提醒一句:不管选哪个,健康检查的间隔和次数一定要根据业务调优。间隔太短徒增开销,间隔太长故障感知慢。做过压测你就会发现,这中间的平衡需要根据实际的 QPS 和响应时间来决定。