Nginx 反向代理与负载均衡配置实战

Nginx 反向代理与负载均衡配置实战

后端从一个 Node 进程扩到三个,发现流量还是全打在第一个上;或者上线新版本时,直接改端口导致用户请求 502 断了一片。这些问题的答案几乎都指向同一个组件:Nginx。这篇不讲概念堆砌,直接把「反向代理怎么配、负载均衡几种策略怎么选、健康检查和灰度怎么做」一次讲透,配置都能直接抄去改。

先分清:正向代理 vs 反向代理

一句话区分:正向代理代理的是客户端 (你连 VPN 出墙,服务端不知道真实的你);反向代理代理的是服务端 (用户访问 api.example.com,不知道背后是哪台机器在处理)。我们做后端部署,用的全是反向代理。

反向代理帮我们做三件事:隐藏后端拓扑、统一入口(SSL 卸载、限流、鉴权都放这层)、把流量分发到多个后端(负载均衡)。

最小可用的反向代理

假设后端服务跑在本机 127.0.0.1:3000,要让外网通过 80 端口访问:

nginx 复制代码
server {
    listen 80;
    server_name api.example.com;

    location / {
        proxy_pass http://127.0.0.1:3000;

        # 下面这几行不是可选项,是必配
        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;
    }
}

新手最常漏的就是那几个 proxy_set_header。不配会踩什么坑?

  • 不传 Host:后端按域名做路由/多租户时会拿到错的 Host,甚至默认变成 127.0.0.1
  • 不传 X-Real-IP / X-Forwarded-For:后端拿到的客户端 IP 全是 Nginx 的 127.0.0.1,你的访问日志、限流、风控全废。
  • 不传 X-Forwarded-Proto:后端不知道用户走的是 HTTPS,生成的重定向/绝对 URL 会变成 http,造成死循环或混合内容。

$proxy_add_x_forwarded_for 会自动把当前的 X-Forwarded-For 追加上游 IP,形成一条 IP 链,比手写 $remote_addr 更正确。

负载均衡:upstream 是核心

要把流量分到多台后端,用 upstream 块定义一组服务器,再 proxy_pass 到这个组:

nginx 复制代码
upstream backend {
    server 127.0.0.1:3000;
    server 127.0.0.1:3001;
    server 127.0.0.1:3002;
}

server {
    listen 80;
    server_name api.example.com;

    location / {
        proxy_pass http://backend;   # 注意这里是 upstream 的名字
        proxy_set_header Host $host;
        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
    }
}

默认策略是轮询(round-robin),请求依次分给三台。这也是开头那个「流量全打第一个」问题的解法------之前没写 upstream,自然不会分发。

四种负载均衡策略怎么选

轮询不是唯一选择,Nginx 内置几种,按场景选:

1. 加权轮询------机器配置不均时用。给强的机器更高权重:

nginx 复制代码
upstream backend {
    server 127.0.0.1:3000 weight=3;   # 这台性能好,分 3 倍流量
    server 127.0.0.1:3001 weight=1;
    server 127.0.0.1:3002 weight=1;
}

2. least_conn(最少连接)------请求处理时长差异大时用。谁当前连接少就给谁,避免慢请求堆在一台上:

nginx 复制代码
upstream backend {
    least_conn;
    server 127.0.0.1:3000;
    server 127.0.0.1:3001;
}

3. ip_hash(按客户端 IP 哈希)------需要会话保持时用。同一个 IP 固定打到同一台后端,适合把 session 存在本机内存、又没上 Redis 共享 session 的老系统:

nginx 复制代码
upstream backend {
    ip_hash;
    server 127.0.0.1:3000;
    server 127.0.0.1:3001;
}

注意 ip_hash 有代价:一旦某台后端下线,落在它上面的所有会话会被重新哈希到别的机器,session 丢失。更推荐的做法还是把 session 外置(Redis),让后端无状态,这样就能自由用轮询了。

4. hash 指定 key------比如按 URL 哈希做缓存亲和:

nginx 复制代码
upstream backend {
    hash $request_uri consistent;   # consistent 用一致性哈希,扩缩容时抖动小
    server 127.0.0.1:3000;
    server 127.0.0.1:3001;
}

健康检查:别把请求发给死掉的后端

开源版 Nginx 没有主动健康检查(定时探活),但有被动健康检查 ,通过 max_failsfail_timeout 实现:

nginx 复制代码
upstream backend {
    server 127.0.0.1:3000 max_fails=3 fail_timeout=30s;
    server 127.0.0.1:3001 max_fails=3 fail_timeout=30s;
    server 127.0.0.1:3002 backup;    # 备用机,主力全挂了才启用
}

含义:某台后端在 fail_timeout=30s 内连续失败 max_fails=3 次,就把它标记为不可用,30 秒内不再往它发请求,之后再试探性放行。backup 标记的机器平时不接流量,只有主力全部不可用时才顶上。

配合超时参数一起用,避免一台慢机器拖垮请求:

nginx 复制代码
location / {
    proxy_pass http://backend;
    proxy_connect_timeout 2s;    # 建连超过 2s 判失败,快速切下一台
    proxy_read_timeout 30s;      # 读响应超时
    proxy_next_upstream error timeout http_502 http_503;  # 遇到这些自动重试下一台
}

proxy_next_upstream 很关键:当前后端返回 502/503 或超时,Nginx 会自动把这个请求转给 upstream 里的下一台,用户无感知。但要注意------非幂等请求(POST)默认也会重试 ,可能导致重复下单,生产上通常要 proxy_next_upstream error timeout; 去掉对 http_5xx 的重试,或明确排除 POST。

灰度发布:用 upstream 做流量切分

上线新版本想先放 10% 流量试水,可以用 split_clients 按比例分流:

nginx 复制代码
# 按请求特征(这里用客户端 IP+UA)哈希,10% 打到新版本
split_clients "${remote_addr}${http_user_agent}" $pool {
    10%     canary;
    *       stable;
}

upstream stable  { server 127.0.0.1:3000; server 127.0.0.1:3001; }
upstream canary  { server 127.0.0.1:3010; }

server {
    listen 80;
    location / {
        proxy_pass http://$pool;   # 变量决定打到哪个 upstream
        proxy_set_header Host $host;
    }
}

同一个用户(同 IP+UA)会稳定命中同一版本,不会一会新一会旧。观察 canary 的错误率和延迟没问题,再把比例逐步调大到 100%。

改完配置怎么安全生效

千万别 nginx -s stop && nginx 重启,那会断掉正在处理的连接。正确流程:

bash 复制代码
nginx -t              # 先测试配置语法,报错就别 reload
nginx -s reload       # 平滑重载:老 worker 处理完存量请求再退出,不断连接

nginx -t 是上线前的保命符------一个花括号没闭合,reload 会失败但老配置还在跑,而如果你直接重启就真挂了。养成先 -treload 的肌肉记忆。

小结

  • 反向代理必配 proxy_set_header:Host、X-Real-IP、X-Forwarded-For、X-Forwarded-Proto,漏了会导致后端拿错 IP、错协议。
  • 负载均衡用 upstream :默认轮询;机器不均用 weight,请求时长差异大用 least_conn,要会话保持用 ip_hash(但更推荐 session 外置做无状态)。
  • 被动健康检查max_fails+fail_timeout,配 backup 备用机;proxy_next_upstream 自动重试,但对 POST 要小心重复提交。
  • 灰度用 split_clients 按比例哈希分流,同用户稳定命中同版本。
  • 改配置永远先 nginx -tnginx -s reload,平滑重载不断连接。

一句话记忆:upstream 管「分给谁」,proxy_set_header 管「怎么转」,-t 再 reload 管「安全生效」------三件事配对了,反向代理就稳了。

相关推荐
zhangrelay16 小时前
笔记本轻量高品质延寿工具完整分系统清单
运维·笔记·学习
RisunJan17 小时前
Linux命令-screen(终端复用器)
linux·运维
啊哦11117 小时前
安装VMware Workstation
linux·运维·服务器
持敬chijing18 小时前
Linux基础命令与目录命令
linux·运维·服务器
小张成长计划..18 小时前
【Linux】12:进程状态
linux·运维·服务器
小秋求学记.21 小时前
Linux_Ubuntu的相关问题
linux·运维·ubuntu
熊猫钓鱼>_>1 天前
ArkTS 方舟编程语言 · 原创快速入门教程
运维·架构·ts·harmonyos·arkts·鸿蒙·js
β添砖java1 天前
黑马Linux笔记
linux·运维·笔记
在水一缸1 天前
当 AI 拥有了“核按钮”:深入解析 MCP 服务器与命令执行护栏
运维·服务器·人工智能·命令执行·智能体·ai安全·mcp