后端节点挂了用户还在等?Nginx 负载均衡与健康检查配置实战
三台后端服务器跑着同一个服务,其中一台挂了,用户请求还是往那台上打——返回 502,体验直接崩了。Nginx 负载均衡不只是把流量分一分,更关键的是在节点出问题时自动摘除流量。这篇文章从 upstream 配置讲到健康检查,给出能直接用的 nginx.conf。
一、最简单的 upstream
先来一个基础配置,三台后端,默认轮询:
upstream backend {
server 10.0.0.1:8080;
server 10.0.0.2:8080;
server 10.0.0.3:8080;
}
server {
listen 80;
server_name api.example.com;
location / {
proxy_pass http://backend;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
}
}
Nginx 默认用轮询(Round Robin),请求按顺序分给三台机器。看起来够用,但实际场景往往没那么简单。
二、加权轮询:机器配置不一样怎么办
三台机器,两台 4 核 8G,一台 8 核 16G,配置不一样,轮询会把大机器浪费掉。加权轮询让大机器多干活:
upstream backend {
server 10.0.0.1:8080 weight=2; # 4核
server 10.0.0.2:8080 weight=2; # 4核
server 10.0.0.3:8080 weight=4; # 8核,权重翻倍
}
权重是相对值,2:2:4 意味着每 8 个请求里,Server1 和 Server2 各拿 2 个,Server3 拿 4 个。
三、IP Hash:需要会话保持的场景
有些服务没做 session 共享(比如老版本的 Java 应用,session 存在本地内存里),同一个用户的请求必须打到同一台机器上。用 ip_hash:
upstream backend {
ip_hash;
server 10.0.0.1:8080;
server 10.0.0.2:8080;
server 10.0.0.3:8080;
}
Nginx 根据客户端 IP 做哈希,固定映射到某台后端。问题是这样负载可能不均匀——如果大量用户来自同一个 NAT 出口 IP,全打到一台机器上。更稳妥的方案是让应用层做 session 共享(Redis 存 session),不要依赖 IP Hash。
四、被动健康检查:让 Nginx 自己摘除坏节点
这是最关键的配置。Nginx 默认有被动健康检查:如果某台后端返回错误或超时,Nginx 会暂时把它标记为不可用。

upstream backend {
server 10.0.0.1:8080 max_fails=3 fail_timeout=30s;
server 10.0.0.2:8080 max_fails=3 fail_timeout=30s;
server 10.0.0.3:8080 max_fails=3 fail_timeout=30s;
}
| 参数 | 含义 | 推荐值 |
|---|---|---|
max_fails |
连续失败多少次后摘除 | 3 |
fail_timeout |
摘除多久后重新尝试 | 30s |
proxy_connect_timeout |
建立连接的超时 | 5s |
proxy_read_timeout |
读取响应的超时 | 10s |
配合 proxy 超时一起用:
server {
listen 80;
server_name api.example.com;
location / {
proxy_pass http://backend;
proxy_connect_timeout 5s;
proxy_read_timeout 10s;
proxy_send_timeout 5s;
# 拿到错误响应后重试下一台
proxy_next_upstream error timeout http_502 http_503 http_504;
proxy_next_upstream_tries 3;
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_next_upstream 这行很重要:某台后端返回 502/503/504 或超时,Nginx 自动把请求转给下一台,用户无感知。proxy_next_upstream_tries 3 限制最多重试 3 次,防止所有节点都挂时无限重试。
五、主动健康检查(需要 nginx_upstream_check_module)
被动健康检查的缺点是:节点挂了之后,要有真实请求打到它上面、失败够次数才会摘除。如果流量低,可能过了好几分钟才发现。
主动健康检查需要第三方模块 nginx_upstream_check_module(淘宝 Tengine 自带,OpenResty 也有),定期主动探测后端健康状态:
upstream backend {
server 10.0.0.1:8080;
server 10.0.0.2:8080;
server 10.0.0.3:8080;
check interval=3000 rise=2 fall=3 timeout=2000 type=http;
check_http_send "HEAD /healthz HTTP/1.0\r\n\r\n";
check_http_expect_alive http_2xx http_3xx;
}
| 参数 | 含义 |
|---|---|
interval=3000 |
每 3 秒检查一次 |
rise=2 |
连续 2 次成功后标记为健康 |
fall=3 |
连续 3 次失败后标记为不健康 |
type=http |
用 HTTP 方式检查 |
check_http_send |
发送的探测请求 |
check_http_expect_alive |
期望的响应状态码 |
后端需要提供一个 /healthz 端点,返回 200 表示健康。这个端点要轻量——不要查 DB,不要查 Redis,只检查进程是否活着。
六、灰度发布:用 split_clients 分流
上线新版本时,先给 10% 的流量试试看,没问题再全量。Nginx 可以用 split_clients 做流量切分:
split_clients "${remote_addr}AAA" $upstream_pool {
10% new_backend;
* backend;
}
upstream backend {
server 10.0.0.1:8080;
server 10.0.0.2:8080;
}
upstream new_backend {
server 10.0.0.4:8080; # 新版本部署在这台
}
server {
listen 80;
location / {
proxy_pass http://$upstream_pool;
}
}
split_clients 根据客户端 IP 做哈希分流,10% 的 IP 固定走 new_backend,剩下走 backend。同一个 IP 每次都走同一个池子,不会在新旧版本之间跳来跳去。
七、长连接复用:别忽略这个性能优化
默认情况下,Nginx 每转发一个请求都新建一条到后端的 TCP 连接。高 QPS 下这个开销不小。开启长连接:
upstream backend {
server 10.0.0.1:8080;
keepalive 32; # 保留 32 条到后端的长连接
}
server {
location / {
proxy_pass http://backend;
proxy_http_version 1.1;
proxy_set_header Connection ""; # 清除 Connection: close
}
}
proxy_http_version 1.1 和 proxy_set_header Connection "" 必须同时设置,缺一个都不行。HTTP/1.1 默认支持长连接,但 Nginx 转发时默认用 HTTP/1.0,要手动升级。
八、完整的配置文件
把上面这些拼起来:
upstream backend {
server 10.0.0.1:8080 weight=2 max_fails=3 fail_timeout=30s;
server 10.0.0.2:8080 weight=2 max_fails=3 fail_timeout=30s;
server 10.0.0.3:8080 weight=4 max_fails=3 fail_timeout=30s;
keepalive 32;
}
server {
listen 80;
server_name api.example.com;
location / {
proxy_pass http://backend;
proxy_http_version 1.1;
proxy_set_header Connection "";
proxy_connect_timeout 5s;
proxy_read_timeout 10s;
proxy_send_timeout 5s;
proxy_next_upstream error timeout http_502 http_503 http_504;
proxy_next_upstream_tries 3;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
}
}
改完记得 reload
nginx -t 先测语法,没问题再 nginx -s reload 热加载,不会断连接。max_fails 和 fail_timeout 的值要根据你的流量调——QPS 高的场景 max_fails 可以设小一点(1 或 2),快速摘除坏节点;QPS 低的场景设大一点,避免偶发网络抖动误摘。proxy_next_upstream 也不是万能的,如果你的接口不是幂等的(比如创建订单),重试会导致重复创建,要么只对 GET 请求开启重试,要么后端做幂等处理。
- 点赞
- 收藏
- 关注作者
评论(0)