一台服务器撑不住的时候,最自然的想法是加机器。但加完机器之后问题来了:用户访问的是同一个域名,凭什么这次请求去 A 机器、下次去 B 机器?这个"分配请求的组件"就是负载均衡器。
这篇文章从原理讲到落地:四层与七层负载均衡的区别到底在哪、Nginx 的 upstream 怎么配、五种调度算法各自适合什么场景、健康检查参数怎么设才不会误伤、会话保持为什么是应该尽量避免的方案、动静分离与 SSL 终结怎么落地、Nginx 做负载均衡与 HAProxy/LVS/云负载均衡的定位差异,以及最后一块——负载均衡器自己挂了怎么办(keepalived + VIP 的思路)。
一、负载均衡解决什么问题
负载均衡(Load Balancing)的核心职责是:把到达的请求按照某种规则分发到后端的多个服务节点,同时对外部隐藏后端的具体拓扑。它带来四个直接收益:
| 收益 | 说明 |
|---|---|
| 提升容量 | 请求被分散,整体处理能力随节点数近似线性增长 |
| 高可用 | 单个节点故障时自动摘除,请求被转发到健康节点 |
| 可维护性 | 可以逐台下线做发布和维护,不影响整体服务 |
| 弹性 | 业务高峰时增加节点、低谷时减少,按需调整 |
需要一开始就明确的是"负载均衡"不等于"高可用"。负载均衡器本身是单点——所有流量都经过它,它挂了整个服务就不可用了。所以完整的方案必须包含负载均衡器自身的冗余(第七节详述)。
几个容易混淆的概念:
- LB(Load Balancer):泛指负载均衡的软件、硬件或服务。
- 反向代理(Reverse Proxy):代表服务端接收请求的代理。负载均衡通常基于反向代理实现,但反向代理的功能不限于负载均衡(还有缓存、SSL 终结、压缩、鉴权等)。
- Upstream / Backend / Real Server:都指后端的真实服务节点,只是不同软件的叫法。Nginx 叫 upstream,HAProxy 叫 backend,LVS 文档里常叫 RS。
二、四层与七层的本质区别
这是选型时最关键的一个分叉点。所谓"四层"是 OSI 模型的传输层(TCP/UDP),"七层"是应用层(HTTP/HTTPS 等)。
| 对比项 | 四层负载均衡(L4) | 七层负载均衡(L7) |
|---|---|---|
| 工作层次 | TCP/UDP,看 IP + 端口 + 协议 | HTTP/HTTPS,看 URL、Header、Cookie、内容 |
| 转发依据 | 目标地址、源地址、端口 | URL 路径、域名、Header、Cookie、请求方法 |
| 是否解析内容 | 不解析,只改写地址后转发 | 完整解析,可按内容做路由决策 |
| 性能 | 极高,接近线速,CPU 开销小 | 较高开销,但现代软件(Nginx)也能达到很高吞吐 |
| 支持的并发 | 百万级(DR 模式下几乎无上限) | 通常数万到数十万 |
| 功能 | 只有转发 | 动静分离、SSL 终结、压缩、重写、限流、灰度、WAF |
| 典型实现 | LVS(DR/NAT/TUN 模式)、云厂商的 CLB/NLB | Nginx、HAProxy、Envoy、云厂商的 ALB |
| 适用位置 | 超大流量的第一级入口 | 业务层、需要内容路由的场景 |
为什么四层性能远高于七层? 因为四层不需要建立完整的应用层连接、不需要解析 HTTP 报文、不需要分配大量的应用层缓冲区。在 LVS 的 DR(Direct Routing)模式下,响应包甚至不经过负载均衡器,而是由后端直接回给客户端,所以吞吐量基本不受 LB 自身带宽限制——这是它能支撑超大流量的根本原因。
反过来,七层能做的事多得多。同一个域名下要把 /api/ 转到后端 API 集群、把 /static/ 交给静态服务、把 /admin/ 限制内网访问,这些只有七层能做到。
选型结论:
- 绝大多数中小规模的业务,直接用 Nginx(七层)就够了。它的性能对十万级 QPS 以下的场景绰绰有余。
- 流量非常大且只需要转发(比如视频、下载、直播),用 LVS 或云厂商的四层 LB 做第一级,后面再接 Nginx 做七层。
- 典型的大型站点会采用分层架构:四层 LB → 七层 Nginx 集群 → 应用集群。上一层做容量,下一层做智能路由。
Nginx 从 1.9 版本开始也支持 stream 模块做四层转发(TCP/UDP),但它的四层能力远不如 LVS 专业,主要用于简单的 TCP 代理场景(如数据库代理、MQ 代理)。
三、Nginx upstream 基础配置
upstream backend {
server 10.0.1.11:8080;
server 10.0.1.12:8080;
server 10.0.1.13:8080;
}
server {
listen 80;
server_name example.com;
location / {
proxy_pass http://backend;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
}
}
三个请求头值得说明,它们决定了后端能不能拿到正确的信息:
Host $host:把原始请求的 Host 头传给后端,否则后端收到的是 upstream 名("backend"),依赖 Host 做路由的应用会出错。X-Real-IP和X-Forwarded-For:传递客户端真实 IP。不配的话后端看到的全部是 Nginx 的 IP,访问日志、风控、地域判断全都会错。X-Forwarded-For用$proxy_add_x_forwarded_for而不是$remote_addr,是为了在多层代理时保留完整的 IP 链。X-Forwarded-Proto $scheme:告诉后端原始请求是 HTTP 还是 HTTPS。如果 Nginx 做了 SSL 终结,后端看到的是 HTTP,没有这个头会导致应用生成的链接回退成 http://。
处理真实 IP 更规范的做法是用 real_ip 模块(当上游还有一层代理时):
# 只有来自 10.0.1.0/24 的代理带来的 XFF 才被信任
set_real_ip_from 10.0.1.0/24;
real_ip_header X-Forwarded-For;
real_ip_recursive on;
注意安全点: 不设 set_real_ip_from 就无条件信任客户端传来的 X-Forwarded-For,等于允许任何人伪造 IP。
四、五种调度算法与选型
Nginx upstream 支持以下几种分发方式:
轮询(round-robin,默认)
upstream backend {
server 10.0.1.11:8080;
server 10.0.1.12:8080;
}
按顺序逐个分配,不考虑任何后端状态。适合所有节点配置相同、请求处理耗时相近的场景。这是默认算法,也是最常用、最不容易出错的一种。
加权轮询(weight)
upstream backend {
server 10.0.1.11:8080 weight=3;
server 10.0.1.12:8080 weight=1;
server 10.0.1.13:8080 weight=1;
}
第一台会分到大约 60%(3/5)的请求。适合节点性能不等的场景(比如新老机器混跑、灰度机器配置较低),也常用于灰度引流——给新版本机器很小的权重观察一段时间。
最少连接(least_conn)
upstream backend {
least_conn;
server 10.0.1.11:8080;
server 10.0.1.12:8080;
}
把新请求交给当前活动连接最少的节点。适合请求处理时间差异较大的场景(例如有大文件上传/导出的接口、响应时间波动大的业务)——轮询在这种场景下会造成"耗时长的请求恰好堆积在某台机器上"。
IP 哈希(ip_hash)
upstream backend {
ip_hash;
server 10.0.1.11:8080;
server 10.0.1.12:8080;
}
按客户端 IP 的前三段(IPv4)计算哈希,同一 IP 的请求固定转发到同一台机器。这是实现会话保持(session sticky)的一种方式,但它是本节最后要劝退的方案,见第六节。
注意 ip_hash 与 weight 可以一起使用,但在某些版本组合下行为有限制。
通用哈希(hash)
upstream backend {
hash $request_uri consistent; # consistent 启用一致性哈希
server 10.0.1.11:8080;
server 10.0.1.12:8080;
}
# 也可以基于任意变量
upstream backend2 {
hash $cookie_userid consistent;
server 10.0.1.11:8080;
server 10.0.1.12:8080;
}
按任意变量哈希,consistent 参数启用一致性哈希——这是关键:普通哈希在节点数量变化时会导致大部分 key 的映射关系改变(缓存大规模失效),一致性哈希只影响少量 key。
选型的对照表:
| 算法 | 配置 | 适合场景 | 注意点 |
|---|---|---|---|
| 轮询 | 默认 | 节点同质、请求耗时接近 | 最安全的选择 |
| 加权轮询 | weight=N |
节点性能不等、灰度引流 | 权重比例要与实际能力匹配 |
| 最少连接 | least_conn |
请求耗时差异大(长任务是其中的一部分) | 短耗时场景意义不大 |
| IP 哈希 | ip_hash |
需要会话保持时的退而求其次方案 | 移动网络/NAT 会导致分布不均 |
| 一致性哈希 | hash $key consistent |
后端有本地缓存、希望提高命中率 | 节点增减时影响最小 |
开源版本的 Nginx 没有商业版提供的 least_time(最少响应时间)算法,需要按响应快慢分发时应考虑 HAProxy 的商业替代或自行实现。
五、健康检查与故障摘除
Nginx 开源版通过被动健康检查判断后端是否存活(商业版支持主动探测)。相关参数:
upstream backend {
server 10.0.1.11:8080 max_fails=3 fail_timeout=10s;
server 10.0.1.12:8080 max_fails=3 fail_timeout=10s;
server 10.0.1.13:8080 backup; # 备用节点
}
| 参数 | 含义 | 说明 |
|---|---|---|
| max_fails | 在 fail_timeout 时间内失败多少次判定为不可用 | 默认 1 |
| fail_timeout | 失败计数的窗口,同时也是被判定不可用后的隔离时长 | 默认 10s |
| backup | 标记为备用节点,只有主节点全挂时才启用 | 灾备、降级页面 |
| down | 标记为永久不可用,便于临时摘除 | 发布时使用 |
| slow_start(商业版) | 节点恢复后逐步增加流量,避免刚启动就被打垮 | 开源版无 |
参数的含义要准确理解:max_fails=3 fail_timeout=10s 的意思是,在 10 秒内连续失败 3 次就认为该节点不可用,并将它摘除 10 秒,10 秒后重新尝试。所以这个组合避免了节点刚恢复就被流量打崩的情况。
什么叫"失败"?可以通过 proxy_next_upstream 定义:
location / {
proxy_pass http://backend;
proxy_next_upstream error timeout http_500 http_502 http_503 http_504;
proxy_next_upstream_tries 3;
proxy_next_upstream_timeout 10s;
}
这条配置的含义是:当请求遇到 error、timeout 或后端返回 500/502/503/504 时,自动把请求重发到下一个节点,最多重试 3 次。
这里有一个非常重要的注意事项:默认配置下 Nginx 会对失败的请求自动重试下一个节点,proxy_next_upstream 默认就包含 error 和 timeout。 对于非幂等的写操作(POST 下单、支付),一次失败后自动重试到另一台,可能导致重复下单、重复扣款。所以涉及资金的接口必须显式处理:
location /api/order {
proxy_pass http://backend;
proxy_next_upstream off; # 写接口关闭自动重试
proxy_read_timeout 30s;
}
或者让应用层保证幂等(更推荐的做法,因为网络层面的超时很难完全避免重试)。
一个常见的痛点:后端只有一台时,proxy_next_upstream 的 timeout 重试会让单次请求的超时变成多倍(重试 3 次 × 每次 30 秒 = 90 秒)。这就是为什么 proxy_read_timeout 和 proxy_next_upstream_tries 要一起考虑。
六、会话保持:能用别的方式就别用
会话保持(session stickiness)是指让同一个用户的请求始终落到同一台后端。它的存在是因为早期应用把 session 存在本地内存/本地文件里,第二次请求落到别的机器上就找不到 session 了。
ip_hash 的四个问题:
- 分布不均:大量用户通过同一个 NAT 出口(公司、学校、移动运营商网关)访问时,哈希会全部落到同一台机器。移动网络尤其严重——用户的 IP 可能在基站间切换,导致 session 频繁丢失。
- 故障时的体验:固定到的那台机器挂了,这些用户的 session 全部失效,需要重新登录。
- 扩缩容不友好:增加或减少节点会改变哈希映射(普通哈希),大量用户的 session 丢失。
- 掩盖了真正的问题:它让"应用有状态"这件事可以继续存在,而这恰恰是阻碍水平扩展的根源。
更好的三种方案,按推荐顺序:
方案一:应用无状态化(最推荐)。 把 session 从本地内存迁移到集中式存储:Redis、Memcached、或者数据库。任何一台机器处理请求都能从共享存储里读到 session。
用户 → Nginx → 任意一台应用节点 → Redis 读 session
这样加机器、减机器、机器挂掉都不影响用户体验,负载均衡可以用最简单的轮询。对于 Spring 应用可以用 Spring Session,PHP 可以在 php.ini 里配 session.save_handler = redis,实现方式都很成熟。
方案二:JWT 等令牌机制。 把状态放在客户端的令牌里(签名保证不可篡改),服务端完全不需要存 session。适合 API 场景。注意令牌大小和无法即时失效这两个特点。
方案三:cookie 路由(次选)。 如果确实做不到无状态,至少要基于应用层标识而不是 IP:
upstream backend {
hash $cookie_jsessionid consistent;
server 10.0.1.11:8080;
server 10.0.1.12:8080;
}
用 consistent(一致性哈希)能在节点增减时把影响降到最小。这比 ip_hash 好,但仍然不如无状态化。
结论:会话保持是一种妥协,不是目标。 只有在遗留系统无法改造时才用它,而且要用基于 cookie 的一致性哈希而不是 ip_hash。
七、动静分离与 SSL 终结
动静分离
静态资源(图片、CSS、JS、视频)和动态请求有不同的特性:静态资源数量多、体积大、可缓存;动态请求 CPU 密集、不可缓存。分开处理能显著提升效率。
upstream app_backend {
least_conn;
server 10.0.1.11:8080;
server 10.0.1.12:8080;
}
upstream static_backend {
server 10.0.1.21:80;
server 10.0.1.22:80;
}
server {
listen 80;
server_name example.com;
# 静态资源:本地直接返回,带长过期时间
location ~* \.(jpg|jpeg|png|gif|ico|webp|css|js|woff2)$ {
root /var/www/static;
expires 30d;
add_header Cache-Control "public, immutable";
access_log off;
open_file_cache max=10000 inactive=30s;
gzip_static on; # 直接使用预压缩的 .gz 文件
}
# 静态资源量特别大时,交给专门的静态集群
location /download/ {
proxy_pass http://static_backend;
proxy_set_header Host $host;
}
# 动态请求走应用集群
location / {
proxy_pass http://app_backend;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
}
}
静态资源量大时,进一步的做法是交给 CDN 或对象存储,把这部分流量完全从自己的服务器上卸掉。
SSL 终结(SSL Termination)
让 Nginx 负责 HTTPS 的加密解密,后端走明文 HTTP:
upstream app_backend {
server 10.0.1.11:8080;
server 10.0.1.12:8080;
}
server {
listen 80;
server_name example.com;
return 301 https://$host$request_uri;
}
server {
listen 443 ssl http2;
server_name example.com;
ssl_certificate /etc/nginx/ssl/example.com.pem;
ssl_certificate_key /etc/nginx/ssl/example.com.key;
ssl_protocols TLSv1.2 TLSv1.3;
ssl_ciphers ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-RSA-AES256-GCM-SHA384;
ssl_prefer_server_ciphers off;
ssl_session_cache shared:SSL:10m;
ssl_session_timeout 1d;
ssl_session_tickets off;
# HSTS(确认全站 HTTPS 后再开启,且要先从小 max-age 开始)
add_header Strict-Transport-Security "max-age=31536000; includeSubDomains" always;
location / {
proxy_pass http://app_backend;
proxy_set_header Host $host;
proxy_set_header X-Forwarded-Proto $scheme; # 重要:告知后端原始协议
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
}
}
SSL 终结的三个好处:
- 后端不用配证书,简化运维,证书更新只需要在一处操作。
- TLS 握手的计算集中在 LB 层,可以集中升级硬件或做专门调优。
- 后端有了明文请求,便于排错和做应用层优化(如内容改写、压缩)。
代价是 LB 到后端这段链路是明文的,所以前提是这段链路在可信网络内(同 VPC、专线、内网)。有合规要求的场景需要在 Nginx 到后端之间再加密(proxy_pass https://...),或者使用 mTLS。
ssl_session_cache 和 TLS 1.3 能有效降低握手开销。X-Forwarded-Proto 不要漏配,否则后端应用生成的重定向 URL 会错误地退回 http:// 造成混合内容警告或重定向循环。
风险提示:修改 add_header Strict-Transport-Security 后,浏览器会在 max-age 期间强制用 HTTPS 访问该域名。如果 HTTPS 还没完全就绪就把 max-age 设得很大(一年),会导致用户无法回退到 HTTP 访问。建议先用较小的值(如 300 秒)验证。
八、Nginx 与 LVS / HAProxy / 云负载均衡的定位
| 方案 | 层次 | 定位 | 什么时候用它 |
|---|---|---|---|
| Nginx | 七层(也有 stream 四层) | 七层负载均衡的事实标准,同时是 Web 服务器和反向代理 | 绝大多数 HTTP 场景;需要按 URL/Header 路由、SSL 终结、动静分离 |
| HAProxy | 四层 + 七层 | 专业负载均衡器,健康检查与状态统计更精细 | 需要更丰富的健康检查策略、更细的调度算法(如 least time)、TCP 层负载均衡 |
| LVS | 四层 | 内核态转发,性能极高,DR 模式可支撑超大流量 | 百万级并发、视频/下载/直播入口的第一级负载 |
| 云负载均衡服务 | 四层 / 七层 | 托管服务,跨可用区高可用,按需付费 | 云上部署,不想自己维护 VIP 和 keepalived;需要跨 AZ 容灾 |
| keepalived + Nginx | 七层 + VIP | 自建高可用方案 | 自建机房,或需要在云上做自定义的高可用策略 |
Nginx 与 HAProxy 的差异(经常被问到):两者七层能力接近,Nginx 的优势在于它同时是 Web 服务器(能直接服务静态文件、做缓存、处理 SSL),生态和资料更丰富;HAProxy 的优势在于更专业的负载均衡特性(丰富的健康检查、统计接口、多种调度算法包括最少响应时间、更细粒度的连接管理)。规模不大、团队熟悉 Nginx 的情况直接用 Nginx 就好。
云负载均衡服务的定位:它解决的最大痛点是高可用——云厂商的 LB 本身就是跨可用区部署的,你不用自己搭 keepalived。缺点是不够灵活、某些高级特性缺失、跨云或混合云场景覆盖不到。云上部署时,通常直接用云 LB 作为入口(负责 VIP 和跨 AZ),后面接自己的 Nginx 集群做七层路由。
九、单点问题:keepalived + VIP
回到开头提到的关键点:只有一台 Nginx 做负载均衡,它故障就等于服务中断。解决方案是两台或多台 Nginx 集群共享一个虚拟 IP(VIP),由 keepalived 通过 VRRP 协议管理这个 VIP 的归属。
典型拓扑(文字描述):
公网 VIP: 203.0.113.10
|
+--------------+--------------+
| |
Nginx LB1 (MASTER) Nginx LB2 (BACKUP)
10.0.1.2, priority 150 10.0.1.3, priority 100
| |
+--------------+--------------+
| (内网 VIP 或直连后端)
+--------------+--------------+
| | |
App1 10.0.1.11 App2 10.0.1.12 App3 10.0.1.13
| | |
+--------------+--------------+
|
Redis / MySQL 集群(共享状态与数据)
工作流程:正常情况下 VIP 绑定在 MASTER(LB1)上,所有流量走 LB1。keepalived 通过 VRRP 心跳持续检测对方存活;当 LB1 宕机或检测到 Nginx 进程异常时,VIP 在几秒内漂移到 BACKUP(LB2),LB2 接管流量,用户几乎无感知(切换期间有短暂中断,通常 1~3 秒)。
MASTER 配置示例:
# /etc/keepalived/keepalived.conf (LB1,MASTER)
global_defs {
router_id LB1
script_user root
enable_script_security
}
vrrp_script chk_nginx {
script "/usr/bin/killall -0 nginx" # 检测 nginx 进程是否存在
interval 2
timeout 2
fall 2 # 连续 2 次失败才认为失败
rise 2
weight -30 # 失败时优先级降低 30
}
vrrp_instance VI_1 {
state MASTER
interface eth0
virtual_router_id 51
priority 150 # BACKUP 设为 100
advert_int 1 # VRRP 通告间隔 1 秒
authentication {
auth_type PASS
auth_pass 一段共享密钥 # 同一 VRRP 组内必须一致
}
track_script {
chk_nginx
}
virtual_ipaddress {
203.0.113.10/24 dev eth0
}
notify_master "/usr/local/bin/notify.sh master"
notify_backup "/usr/local/bin/notify.sh backup"
notify_fault "/usr/local/bin/notify.sh fault"
}
BACKUP 的差别只有 state BACKUP 和 priority 100,其余保持一致(特别是 virtual_router_id 和 auth_pass)。
安装与启动:
sudo dnf install -y keepalived # RHEL、Rocky、CentOS
sudo apt install -y keepalived # Debian、Ubuntu
sudo systemctl enable --now keepalived
ip addr show eth0 | grep -E 'inet|203.0.113.10' # 确认 VIP 在谁身上
journalctl -u keepalived -f # 观察切换日志
防火墙必须放行 VRRP 协议(协议号 112),这是最常见的配置失败原因——两台机器都起来了但互相收不到通告,结果是两台都抢到 VIP(脑裂):
# RHEL、Rocky、CentOS(firewalld)
sudo firewall-cmd --permanent --add-rich-rule='rule protocol value="vrrp" accept'
sudo firewall-cmd --reload
# Debian、Ubuntu(ufw 需要在 before.rules 里加,或直接用 nftables)
sudo ufw allow in proto vrrp from 10.0.1.0/24 comment 'keepalived VRRP'
脑裂(split-brain)是关键风险:两台机器都认为自己是 MASTER 并绑定 VIP,导致流量混乱。原因通常是心跳网络不通(防火墙未放行 VRRP、网卡故障)或者两台机的 VRRP 参数不一致。防护措施除了解除网络故障外,还可以使用双心跳链路、以及在云环境下(不支持 VRRP 组播时)改用云厂商的负载均衡或 keepalived 的单播模式:
# 云环境下常用单播(很多云网络不支持组播)
unicast_src_ip 10.0.1.2
unicast_peer {
10.0.1.3
}
注意:公有云环境通常不支持自定义的 VIP 漂移(ARP 受管控),这种情况下应当直接使用云厂商提供的负载均衡服务,而不是自建 keepalived。这是"云上选型"的重要原则。
下线维护的正确姿势:不要直接关掉 keepalived 所在的机器。应该调低优先级或停服务让 VIP 平滑漂移:
sudo systemctl stop keepalived # VIP 会漂到 BACKUP
ip addr show eth0 # 确认 VIP 已经不在本机
# 维护完成后
sudo systemctl start keepalived
常见问题
该选用四层还是七层负载均衡?
看需不需要"按内容路由"。需要按 URL、域名、Header 做分发,需要 SSL 终结、压缩、动静分离,就用七层(Nginx/HAProxy)。只是单纯把超大流量(视频、下载、直播)分给后端,不需要解析内容,用四层(LVS 或云厂商四层 LB)性能更好。大型站点通常两者结合:四层做第一级入口,七层做业务路由。
Nginx 能支撑多少并发?
取决于硬件、请求内容和配置。一般的 4~8 核机器做纯反向代理,处理简单接口的 QPS 在几千到几万量级;静态小文件可以更高。不要相信笼统的"能支持十万并发"的说法,要用压测在自己的真实请求上测出来。另外反向代理场景每个请求占用两个 fd,估算上限时要除以 2(详见本系列 Nginx 并发优化篇)。
ip_hash 做会话保持有什么问题?
三个主要问题:一是大量用户共享出口 NAT 导致请求集中到某台机器,分布严重不均;二是移动网络下用户 IP 会变,session 还是会丢;三是扩缩容时会破坏哈希映射导致大批 session 失效。建议改造应用做到无状态(session 存 Redis),实在改不了就用基于 cookie 的一致性哈希。
为什么后端一台没挂,请求还是会失败?
先看 proxy_next_upstream 的行为:默认情况下 error 和 timeout 会触发重试。如果 proxy_read_timeout 是 30 秒而后端某次处理超过 30 秒,Nginx 会认为超时并重试,总耗时可能累积到几十秒最终返回 504。解决办法是把慢接口单独配置更长的 timeout,或者优化这个接口本身。另外要检查 worker 连接数/backlog 是否是瓶颈(前面 Nginx 并发篇有详细说明)。
写放在 backup 节点上,平时是不是浪费?
backup 节点的设计目的就是"主节点全挂时才启用",所以它平时的闲置是预期的。实践中可以把备用节点配置得比主节点低(节省成本),或者让它承载一些低优先级的任务(如后台跑批处理、接收内部管理流量),这样不算完全浪费。也可以采用双活方案——两台 Nginx 互为主备,各自承载一部分业务的 VIP,平时都工作。
云上还需要自己搭 keepalived 吗?
一般不需要,也不推荐。公有云的网络通常不支持自定义 VIP 的 ARP 漂移,强行搭会遇到各种限制。雲厂商的负载均衡服务本身就是分布式、跨可用区高可用的,直接作为入口使用更合适,后面接自己的 Nginx 集群做七层路由。自建 keepalived 主要适用于自建机房、裸金属或特殊的混合云场景。
健康检查用 Nginx 自带的 max_fails 够吗?
对多数场景够用,但它是被动的——只有真的有请求打过去失败了才会发现。如果希望主动探测(定期发健康检查请求),Nginx 开源版本身不支持,可以考虑:用 HAProxy 替代、使用第三方 nginx_upstream_check_module 模块(需要自行编译)、或者在外部用一个探活脚本定期修改 upstream 配置。商业版 Nginx Plus 提供了主动健康检查能力。
总结
- 负载均衡解决四件事:提升容量、高可用(摘除故障节点)、可维护(逐台下线发布)、弹性扩缩。但它本身是单点,完整方案必须包含 LB 自身的冗余(keepalived + VIP 或云负载均衡)。
- 四层(L4)看 IP+端口,性能极高(LVS 的 DR 模式下响应包不经过 LB),但只能转发;七层(L7)能解析 HTTP,支持按 URL/Header/Cookie 路由、SSL 终结、动静分离、限流。绝大多数中小业务直接用 Nginx 七层就够;超大流量用四层做第一级、七层做第二级。
- Nginx upstream 的调度算法:轮询(默认,节点同质时最安全)、加权轮询(节点性能不等或灰度引流)、least_conn(请求耗时差异大时)、ip_hash 与一致性哈希(会话保持的妥协方案)。开源版没有 least_time。
- 健康检查参数
max_fails=3 fail_timeout=10s的正确理解是:10 秒内失败 3 次则摘除 10 秒后重试。proxy_next_upstream默认会对 error/timeout 自动重试,写接口(下单、支付)必须proxy_next_upstream off或由应用层保证幂等,否则可能造成重复下单。 - 会话保持是妥协不是目标:ip_hash 在 NAT 和移动网络下分布严重不均、故障时 session 全部失效、扩缩容会破坏映射。优先做应用无状态化(session 存 Redis),次选基于 cookie 的一致性哈希。
- 动静分离把静态资源从应用层卸下来(
expires、open_file_cache、gzip_static,量大就上 CDN/对象存储);SSL 终结把 TLS 计算和证书管理集中在 LB 层,但必须配X-Forwarded-Proto,否则后端生成的链接会错误地回到 http。HSTS 的 max-age 要从小值开始验证。 - 选型对照:Nginx 是七层的事实标准且兼做 Web 服务器;HAProxy 健康检查与调度算法更专业;LVS 是超高性能的四层方案;云负载均衡服务的最大价值是自带跨可用区高可用,云上不必自建 keepalived。
- keepalived 通过 VRRP 管理 VIP(MASTER priority 150 / BACKUP 100),配合
vrrp_script检测 Nginx 进程可实现秒级切换。防火墙必须放行 VRRP 协议(协议号 112),否则两台机器互相收不到心跳会造成脑裂。云环境不支持组播时用unicast_peer单播模式,或者直接使用云厂商的负载均衡。下线维护用systemctl stop keepalived让 VIP 平滑漂移,不要直接关机。
企业QQ咨询




