后端节点挂了用户还在等?Nginx 负载均衡与健康检查配置实战

举报
海是岛思念的泪 发表于 2026/09/16 09:30:43 2026/09/16
【摘要】 从upstream配置到加权轮询、IP Hash、被动健康检查,讲清楚 Nginx 负载均衡的选型逻辑和常见坑,附完整的 nginx.conf 示例。

三台后端服务器跑着同一个服务,其中一台挂了,用户请求还是往那台上打——返回 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 会暂时把它标记为不可用。

image.png

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 请求开启重试,要么后端做幂等处理。

【声明】本内容来自华为云开发者社区博主,不代表华为云及华为云开发者社区的观点和立场。转载时必须标注文章的来源(华为云社区)、文章链接、文章作者等基本信息,否则作者和本社区有权追究责任。如果您发现本社区中有涉嫌抄袭的内容,欢迎发送邮件进行举报,并提供相关证据,一经查实,本社区将立刻删除涉嫌侵权内容,举报邮箱: cloudbbs@huaweicloud.com
  • 点赞
  • 收藏
  • 关注作者

评论(0)

0/1000
抱歉,系统识别当前为高风险访问,暂不支持该操作

全部回复

上滑加载中

设置昵称

在此一键设置昵称,即可参与社区互动!

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。