前言

Nginx 一半的生命在当"流量二传手":把请求转发给后端服务,并把多台后端"捏"成一个池子。这篇讲透反向代理与负载均衡的配置细节。

一、正向 vs 反向代理

正向代理(代理客户端):                反向代理(代理服务端):
客户端 → [代理] → 互联网              客户端 → [Nginx] → 后端集群
翻墙软件、公司出口                      负载均衡、SSL卸载、缓存
服务器不知道真实客户端                  客户端不知道真实后端

二、反向代理基础:proxy_pass

upstream app {
    server 127.0.0.1:8080;
    server 127.0.0.1:8081;
}

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

    location / {
        proxy_pass http://app;

        # ---- 标准转发头(几乎必配)----
        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_connect_timeout 5s;
        proxy_read_timeout    60s;      # 后端慢接口调大
        proxy_send_timeout    30s;
        proxy_buffering on;             # 响应先缓冲再发(默认)
    }
}

proxy_pass 的斜杠陷阱:

# 带路径的 proxy_pass: /api/xxx -> 后端 /v1/xxx(替换 location 前缀)
location /api/ {
    proxy_pass http://app/v1/;
}

# 不带路径(只有 host): 原样透传 /api/xxx
location /api/ {
    proxy_pass http://app;
}

给后端传真实客户端 IP:后端拿到的 RemoteAddr 是 Nginx。后端应用需读 X-Real-IP 或解析 X-Forwarded-For(第一个 IP 是最初客户端)。

三、upstream 负载均衡策略

# 1. 轮询(默认) + 权重
upstream app {
    server 10.0.1.11:8080 weight=3;     # 扛 3/4 流量(机器好就多分)
    server 10.0.1.12:8080 weight=1;
}

# 2. ip_hash: 同一客户端 IP 恒定打到同一后端(会话保持的土办法)
upstream app {
    ip_hash;
    server 10.0.1.11:8080;
    server 10.0.1.12:8080;
}

# 3. least_conn: 转给当前连接数最少的(长短连接混合场景更公平)
upstream app {
    least_conn;
    server 10.0.1.11:8080;
    server 10.0.1.12:8080;
}

server 参数(被动健康检查):

upstream app {
    server 10.0.1.11:8080 max_fails=3 fail_timeout=30s;
    # 30 秒内失败 3 次 → 标记不可用 30 秒
    server 10.0.1.12:8080 backup;       # 备胎: 全挂了才上
    server 10.0.1.13:8080 down;         # 手动摘除(维护模式)
}

开源版 Nginx 只有被动检查(靠真实流量发现失败);主动健康检查需要 Nginx Plus 或 tengine,或改用 keepalived+VIP / K8s Ingress 的方案。

四、典型场景配置

动静分离

server {
    listen 80;
    server_name example.com;

    location /static/ {                  # 静态: 本地直出
        root /var/www;
        expires 30d;
    }
    location /api/ {                     # 动态: 转后端
        proxy_pass http://app;
    }
}

路径重写

# 老路径迁移
location /old/ {
    rewrite ^/old/(.*)$ /new/$1 permanent;    # 301
}

# 带正则的转发
location ~ ^/v(\d+)/ {
    proxy_pass http://app;                     # 正则 location 里 proxy_pass 不能带 URI
    proxy_set_header X-API-Version $1;         # 捕获组传给后端
}

本地缓存(给慢后端减负)

proxy_cache_path /var/cache/nginx levels=1:2 keys_zone=api_cache:10m max_size=1g inactive=10m;

server {
    location /api/public/ {
        proxy_pass http://app;
        proxy_cache api_cache;
        proxy_cache_valid 200 5m;              # 200 响应缓存 5 分钟
        proxy_cache_key $uri$is_args$args;
        add_header X-Cache-Status $upstream_cache_status;   # HIT/MISS 调试头
    }
}

四层代理(TCP/UDP,转发 MySQL/Redis)

# nginx.conf 顶层(与 http 块平级)
stream {
    upstream mysql {
        server 10.0.2.21:3306;
    }
    server {
        listen 3306;
        proxy_pass mysql;
    }
}

WebSocket

location /ws/ {
    proxy_pass http://app;
    proxy_http_version 1.1;
    proxy_set_header Upgrade    $http_upgrade;     # 协议升级头
    proxy_set_header Connection "upgrade";
    proxy_read_timeout 3600s;                       # 长连接别太早掐
}

五、验证与排障

nginx -t && systemctl reload nginx

# 看负载均衡实际效果
for i in $(seq 10); do curl -s http://api.example.com/backend-id; done
# 轮询应轮流出现不同后端标识

# 关键日志变量(加进 log_format 排障神器)
log_format upstream '$remote_addr -> $upstream_addr '
                    'status=$status ups=$upstream_status rt=$upstream_response_time';
# 10.0.0.5 -> 10.0.1.11:8080 status=200 ups=200 rt=0.012

# 常见错误码
# 502: 后端连接失败(进程挂/端口错/防火墙)
# 504: 后端超时(proxy_read_timeout 调大或优化后端)
# 499: 客户端等不及主动断开(后端太慢)

502 排查链路:curl 后端端口 → ss -tlnp 看监听 → /var/log/nginx/error.log 看具体 connect 错误。

六、架构位置示意

用户 ─► DNS/CDN ─► [LB/VIP] ─► Nginx 集群(七层: TLS/路由/缓存)
                                      │
                                      ├─► 静态文件(本地/对象存储)
                                      ├─► App 集群(upstream 轮询)
                                      └─► 内部服务(stream 四层转发)

小结

需求 配置
转发 proxy_pass + 四个 proxy_set_header
负载策略 weight / ip_hash / least_conn
健康检查 max_fails + fail_timeout(被动)
会话保持 ip_hash(或应用层 token)
真实 IP X-Real-IP / X-Forwarded-For
排障 $upstream_addr/$upstream_response_time 写进日志

本文是「Linux 运维」系列第 9 篇。