55 - Nginx 反向代理与负载均衡

Nginx 不仅是高性能 Web 服务器,更是工业级的反向代理和负载均衡器。通过 proxy_pass 将请求转发到后端应用,通过 upstream 块实现多节点负载分发,通过健康检查自动剔除故障节点,通过缓存和限流保护后端——这些能力使 Nginx 成为现代微服务架构中不可或缺的反向代理层。本章在 26-Web服务器(Nginx+Apache) 的基础上深入反向代理、负载均衡和生产实践。


55.1 反向代理概念

正向代理 vs 反向代理

正向代理(Forward Proxy) 反向代理(Reverse Proxy)
 客户端 → 代理 → 互联网 客户端 → 代理 → 后端服务器
 隐藏客户端身份 隐藏服务器身份
 
 [Client A] ─┐ [Client A] ─┐
 [Client B] ─┤→ [Proxy] → [Internet] ├→ [Nginx] ─┬→ [Server 1]
 [Client C] ─┘ │ ├→ [Server 2]
 [Client B] ───┘ └→ [Server 3]

反向代理的核心作用

作用说明
负载均衡将请求分发到多台后端服务器
SSL 终止在代理层解密 HTTPS,后端用 HTTP
缓存缓存静态/动态内容,减少后端负载
安全隔离隐藏后端真实 IP,统一安全策略
压缩gzip/brotli 压缩响应
路由按 URL 路径、域名分发到不同服务
限流防止单客户端过度请求
WebSocket代理 WebSocket 长连接

55.2 proxy_pass 基础

基本配置

# /etc/nginx/conf.d/reverse-proxy.conf
 
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_pass 的 URL 尾斜杠规则

# 带 / — 替换匹配的前缀
location /api/ {
 proxy_pass http://backend/;
 # 请求 /api/users → 转发到 http://backend/users
}
 
# 不带 / — 保留完整路径
location /api/ {
 proxy_pass http://backend;
 # 请求 /api/users → 转发到 http://backend/api/users
}

常用 proxy_set_header

# 向后端传递客户端真实信息的标准头集合
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 X-Forwarded-Host $host;
proxy_set_header X-Forwarded-Port $server_port;
 
# 对于 WebSocket
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection "upgrade";

proxy 超时设置

location / {
 proxy_pass http://backend;
 proxy_connect_timeout 60s; # 连接后端超时
 proxy_send_timeout 60s; # 发送请求到后端超时
 proxy_read_timeout 120s; # 等待后端响应超时
 proxy_buffering on;
 proxy_buffer_size 4k;
 proxy_buffers 8 16k;
 proxy_busy_buffers_size 32k;
}

55.3 upstream 负载均衡

负载均衡方法

Nginx 支持以下负载均衡算法:

方法指令说明
轮询(默认)无需额外指令按顺序依次分配
加权轮询weight=N按权重比例分配
最少连接least_conn分发给当前连接数最少的后端
IP 哈希ip_hash同一客户端 IP 始终路由到同一后端
通用哈希hash $variable按指定变量哈希分配
随机random随机选择(可配合 two 参数:选两个,择一)

upstream 块配置

upstream backend_servers {
 # 轮询(默认)
 server 192.168.1.10:8080;
 server 192.168.1.11:8080;
 server 192.168.1.12:8080;
}
 
server {
 listen 80;
 server_name app.example.com;
 
 location / {
 proxy_pass http://backend_servers;
 proxy_set_header Host $host;
 proxy_set_header X-Real-IP $remote_addr;
 }
}

加权轮询

upstream backend_weighted {
 server 192.168.1.10:8080 weight=5; # 50% 流量
 server 192.168.1.11:8080 weight=3; # 30% 流量
 server 192.168.1.12:8080 weight=2; # 20% 流量
}

最少连接

upstream backend_leastconn {
 least_conn;
 server 192.168.1.10:8080;
 server 192.168.1.11:8080;
 server 192.168.1.12:8080;
}

IP 哈希(Session 保持)

upstream backend_iphash {
 ip_hash;
 server 192.168.1.10:8080;
 server 192.168.1.11:8080;
 server 192.168.1.12:8080;
}

注意:ip_hash 只能基于前 3 个八位组(IPv4)或整个 IPv6 地址。当某台后端宕机时,该后端的流量会重新分配到其他节点。

一致性哈希(hash)

upstream backend_hash {
 hash $request_uri consistent;
 server 192.168.1.10:8080;
 server 192.168.1.11:8080;
 server 192.168.1.12:8080;
}

consistent 参数使用 ketama 一致性哈希算法,增加/移除节点时只有少量请求的哈希映射会改变,适合缓存场景。


55.4 后端健康检查

被动健康检查(Nginx OSS 版)

upstream backend_health {
 server 192.168.1.10:8080 max_fails=3 fail_timeout=30s;
 server 192.168.1.11:8080 max_fails=3 fail_timeout=30s;
 server 192.168.1.12:8080 max_fails=3 fail_timeout=30s backup;
 server 192.168.1.13:8080 down;
}
参数说明
max_fails=Nfail_timeout 内失败 N 次后标记为不可用
fail_timeout=Ns失败时间窗口 + 标记不可用的时长
backup备用服务器,仅在其他全部不可用时启用
down手动标记为不可用(维护窗口)

主动健康检查(Nginx Plus / 模块)

# Nginx Plus 商业版支持主动健康检查
# 开源版可使用 nginx_upstream_check 模块(需自行编译)
upstream backend_active {
 server 192.168.1.10:8080;
 server 192.168.1.11:8080;
 
 check interval=3000 rise=2 fall=5 timeout=1000 type=http;
 check_http_send "HEAD /health HTTP/1.0\r\n\r\n";
 check_http_expect_alive http_2xx http_3xx;
}

使用 map + 自定义健康端点

# 开源版变通方案:定义一个健康检查 location
location /nginx-health {
 access_log off;
 return 200 "healthy\n";
 add_header Content-Type text/plain;
}

使用外部工具(如 curl、HAProxy、Keepalived)定期探测此端点。


55.5 SSL 终止

单站点 SSL 配置

server {
 listen 443 ssl http2;
 server_name api.example.com;
 
 ssl_certificate /etc/nginx/ssl/api.example.com.crt;
 ssl_certificate_key /etc/nginx/ssl/api.example.com.key;
 
 # Let's Encrypt 证书(推荐)
 # ssl_certificate /etc/letsencrypt/live/api.example.com/fullchain.pem;
 # ssl_certificate_key /etc/letsencrypt/live/api.example.com/privkey.pem;
 
 ssl_protocols TLSv1.2 TLSv1.3;
 ssl_ciphers 'ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256';
 ssl_prefer_server_ciphers on;
 ssl_session_cache shared:SSL:10m;
 ssl_session_timeout 10m;
 
 location / {
 proxy_pass http://backend_servers;
 proxy_set_header Host $host;
 proxy_set_header X-Forwarded-Proto https;
 proxy_set_header X-Real-IP $remote_addr;
 }
}
 
# HTTP → HTTPS 重定向
server {
 listen 80;
 server_name api.example.com;
 return 301 https://$host$request_uri;
}

多站点 SSL(SNI)

# Nginx 自动根据 SNI 选择合适的证书
server {
 listen 443 ssl http2;
 server_name site-a.example.com;
 ssl_certificate /etc/nginx/ssl/site-a.crt;
 ssl_certificate_key /etc/nginx/ssl/site-a.key;
 # ...
}
 
server {
 listen 443 ssl http2;
 server_name site-b.example.com;
 ssl_certificate /etc/nginx/ssl/site-b.crt;
 ssl_certificate_key /etc/nginx/ssl/site-b.key;
 # ...
}

55.6 WebSocket 代理

server {
 listen 443 ssl http2;
 server_name ws.example.com;
 
 ssl_certificate /etc/letsencrypt/live/ws.example.com/fullchain.pem;
 ssl_certificate_key /etc/letsencrypt/live/ws.example.com/privkey.pem;
 
 location /ws/ {
 proxy_pass http://ws_backend;
 proxy_http_version 1.1;
 
 # WebSocket 必需的 Upgrade 头
 proxy_set_header Upgrade $http_upgrade;
 proxy_set_header Connection "upgrade";
 
 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;
 
 # WebSocket 长连接超时
 proxy_read_timeout 3600s;
 proxy_send_timeout 3600s;
 }
}
 
upstream ws_backend {
 server 127.0.0.1:3001;
 server 127.0.0.1:3002;
}

55.7 代理层缓存

# 定义缓存路径
proxy_cache_path /var/cache/nginx levels=1:2 keys_zone=api_cache:100m
 max_size=10g inactive=60m use_temp_path=off;
 
server {
 listen 80;
 server_name api.example.com;
 
 # 通用缓存配置
 proxy_cache api_cache;
 proxy_cache_valid 200 302 10m;
 proxy_cache_valid 404 1m;
 proxy_cache_use_stale error timeout updating http_500 http_502 http_503;
 proxy_cache_lock on;
 proxy_cache_background_update on;
 
 # 缓存 bypass 头(调试用)
 proxy_cache_bypass $http_cache_control;
 
 # 向客户端返回缓存状态头
 add_header X-Cache-Status $upstream_cache_status;
 
 location / {
 proxy_pass http://backend_servers;
 proxy_set_header Host $host;
 }
 
 # 特定路径更长的缓存时间
 location /static/ {
 proxy_pass http://backend_servers;
 proxy_cache_valid 200 1h;
 }
 
 # 不缓存的路径
 location /api/private/ {
 proxy_pass http://backend_servers;
 proxy_cache off;
 }
}

缓存状态变量

$upstream_cache_status 的可能值:

说明
MISS缓存未命中,请求已转发到后端
HIT缓存命中
EXPIRED缓存已过期,请求已转发到后端
STALE使用过期的缓存(后端不可用时)
UPDATING已在更新,使用过期缓存响应
BYPASS缓存被跳过
REVALIDATED缓存已重新验证(by If-Modified-Since 等)

55.8 速率限制

limit_req_zone(请求速率限制)

# 定义速率限制区域(10MB 共享内存,基于客户端 IP)
limit_req_zone $binary_remote_addr zone=api_limit:10m rate=10r/s;
 
# 对登录接口做更严格的限制
limit_req_zone $binary_remote_addr zone=login_limit:10m rate=1r/m;
 
server {
 listen 80;
 server_name api.example.com;
 
 # API 全局限流:10 请求/秒,允许额外 5 个突发
 location /api/ {
 limit_req zone=api_limit burst=5 nodelay;
 proxy_pass http://backend_servers;
 }
 
 # 登录接口严格限流:1 请求/分钟
 location /api/login {
 limit_req zone=login_limit burst=2 nodelay;
 proxy_pass http://backend_servers;
 }
 
 # 带宽限制(下载限速)
 location /downloads/ {
 limit_rate 500k;
 limit_rate_after 2m;
 proxy_pass http://backend_servers;
 }
}

limit_conn(并发连接限制)

limit_conn_zone $binary_remote_addr zone=conn_limit:10m;
 
server {
 location / {
 limit_conn conn_limit 10; # 每 IP 最多 10 并发连接
 limit_conn_status 429; # 超限返回 429
 proxy_pass http://backend_servers;
 }
}

限流日志

# 定义限流日志格式
limit_req_log_level warn;
log_format rate_limit '$remote_addr - $request - rate_limit_rejected';
 
server {
 # 限流达到时记录
 location /api/ {
 limit_req zone=api_limit burst=5 nodelay;
 limit_req_log_level error;
 proxy_pass http://backend_servers;
 }
}

55.9 常见反向代理模式

模式 1:微服务路由(按 URL 路径)

server {
 listen 443 ssl http2;
 server_name services.example.com;
 
 # API 鉴权服务
 location /api/auth/ {
 proxy_pass http://auth_service;
 }
 
 # 用户服务
 location /api/users/ {
 proxy_pass http://user_service;
 }
 
 # 订单服务
 location /api/orders/ {
 proxy_pass http://order_service;
 }
 
 # 文件服务(直通上传)
 location /api/files/ {
 proxy_pass http://file_service;
 client_max_body_size 100m; # 大文件上传
 proxy_request_buffering off; # 禁用请求缓冲
 }
}
 
# 各服务 upstream 定义
upstream auth_service {
 server 10.0.0.10:3000;
 server 10.0.0.11:3000;
}
 
upstream user_service {
 server 10.0.0.20:4000;
 server 10.0.0.21:4000;
}
 
upstream order_service {
 server 10.0.0.30:5000;
 server 10.0.0.31:5000;
}
 
upstream file_service {
 server 10.0.0.40:6000;
}

模式 2:API 网关(多域名 + 路径路由)

# 主 API 网关配置
server {
 listen 443 ssl http2;
 server_name api.example.com;
 
 # 认证后添加自定义头
 location / {
 auth_request /internal/auth;
 auth_request_set $user_id $upstream_http_x_user_id;
 
 proxy_set_header X-User-ID $user_id;
 proxy_pass http://microservices;
 }
 
 # 内部认证端点
 location = /internal/auth {
 internal;
 proxy_pass http://auth_service/verify;
 proxy_pass_request_body off;
 proxy_set_header Content-Length "";
 proxy_set_header X-Original-URI $request_uri;
 }
}
 
upstream microservices {
 hash $request_uri consistent;
 server 10.0.0.100:8080;
 server 10.0.0.101:8080;
}

模式 3:前后端分离(SPA + API)

server {
 listen 443 ssl http2;
 server_name app.example.com;
 
 root /var/www/app/dist;
 
 # API 请求转发到后端
 location /api/ {
 proxy_pass http://backend_servers;
 proxy_set_header Host $host;
 proxy_set_header X-Real-IP $remote_addr;
 proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
 }
 
 # SPA 静态文件
 location / {
 try_files $uri $uri/ /index.html;
 }
 
 # 静态资源长期缓存
 location /assets/ {
 expires 1y;
 add_header Cache-Control "public, immutable";
 }
}

55.10 实战:Nginx 前端 + 多语言后端

场景描述

一个生产环境部署:Nginx 作为入口,前端为静态 React 应用,后端有 Python Flask(端口 5000)、Node.js Express(端口 3000)、内部管理服务(端口 8080)。

# /etc/nginx/conf.d/production.conf
 
# === 全局限流配置 ===
limit_req_zone $binary_remote_addr zone=global:10m rate=30r/s;
limit_req_zone $binary_remote_addr zone=login:10m rate=1r/m;
 
# === 后端 upstream 组 ===
upstream flask_api {
 least_conn;
 server 127.0.0.1:5000 max_fails=3 fail_timeout=30s;
 server 127.0.0.1:5001 max_fails=3 fail_timeout=30s;
 keepalive 32;
}
 
upstream nodejs_ws {
 ip_hash;
 server 127.0.0.1:3000 max_fails=2 fail_timeout=15s;
 server 127.0.0.1:3001 max_fails=2 fail_timeout=15s;
}
 
upstream admin_panel {
 server 127.0.0.1:8080;
}
 
# === 主站点 ===
server {
 listen 443 ssl http2;
 server_name mysite.com;
 
 ssl_certificate /etc/letsencrypt/live/mysite.com/fullchain.pem;
 ssl_certificate_key /etc/letsencrypt/live/mysite.com/privkey.pem;
 
 root /var/www/mysite/dist;
 index index.html;
 charset utf-8;
 
 # 全局限流
 limit_req zone=global burst=10 nodelay;
 
 # 前端 SPA
 location / {
 try_files $uri $uri/ /index.html;
 }
 
 location /assets/ {
 expires 1y;
 add_header Cache-Control "public, immutable";
 }
 
 # Python Flask REST API
 location /api/v1/ {
 proxy_pass http://flask_api;
 proxy_http_version 1.1;
 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 Connection "";
 }
 
 # 登录接口 — 严格限流
 location = /api/v1/login {
 limit_req zone=login burst=2 nodelay;
 proxy_pass http://flask_api;
 proxy_set_header Host $host;
 proxy_set_header X-Real-IP $remote_addr;
 }
 
 # Node.js WebSocket
 location /ws/ {
 proxy_pass http://nodejs_ws;
 proxy_http_version 1.1;
 proxy_set_header Upgrade $http_upgrade;
 proxy_set_header Connection "upgrade";
 proxy_set_header Host $host;
 proxy_set_header X-Real-IP $remote_addr;
 proxy_read_timeout 3600s;
 }
 
 # 内部管理后台(仅允许内网访问)
 location /admin/ {
 allow 10.0.0.0/8;
 allow 192.168.0.0/16;
 deny all;
 
 proxy_pass http://admin_panel;
 proxy_set_header Host $host;
 proxy_set_header X-Real-IP $remote_addr;
 }
}
 
# === HTTP 重定向到 HTTPS ===
server {
 listen 80;
 server_name mysite.com;
 return 301 https://$host$request_uri;
}

启动与验证

# 启动后端服务
cd /opt/app/flask && gunicorn -w 4 -b 127.0.0.1:5000 app:app &
cd /opt/app/flask2 && gunicorn -w 4 -b 127.0.0.1:5001 app:app &
cd /opt/app/nodejs && node server.js & # 监听 127.0.0.1:3000
cd /opt/app/nodejs2 && node server.js & # 监听 127.0.0.1:3001
 
# 测试 Nginx 配置
sudo nginx -t
 
# 重载配置
sudo nginx -s reload
 
# 验证后端可达
curl -I https://mysite.com/api/v1/health
curl -I -H "Upgrade: websocket" -H "Connection: upgrade" https://mysite.com/ws/

55.11 调试与监控反向代理

Nginx 访问日志增强

# 自定义日志格式,包含 upstream 相关信息
log_format upstream_info '$remote_addr - $remote_user [$time_local] '
 '"$request" $status $body_bytes_sent '
 '"$http_referer" "$http_user_agent" '
 'upstream=[$upstream_addr] '
 'rt=$upstream_response_time '
 'uct=$upstream_connect_time '
 'uht=$upstream_header_time '
 'cache=$upstream_cache_status';
 
access_log /var/log/nginx/access.log upstream_info;

实时监控工具

# 查看哪个 backend 收到最多请求
tail -f /var/log/nginx/access.log | grep -oP 'upstream=\[\K[^\]]+' | sort | uniq -c
 
# 查看响应时间分布(慢请求排查)
tail -f /var/log/nginx/access.log | grep -oP 'rt=\K[0-9.]+' | sort -n
 
# 使用 ngxtop(实时 Nginx 访问统计)
sudo apt install python3-pip -y
pip install ngxtop
ngxtop -l /var/log/nginx/access.log
 
# 查看 Nginx stub_status 模块的状态
# 在配置中添加:
# location /nginx_status {
# stub_status on;
# access_log off;
# allow 127.0.0.1;
# deny all;
# }
curl http://127.0.0.1/nginx_status
# Active connections: 291
# server accepts handled requests
# 16630948 16630948 31070465
# Reading: 6 Writing: 179 Waiting: 106

stub_status 指标解读

指标说明
Active connections当前活动连接数(含等待)
accepts已接受的总连接数
handled已处理的总连接数
requests已处理的总请求数
Reading正在读取请求头
Writing正在读取请求体/处理/发送响应
Waiting保持连接(keep-alive)等待中

55.12 高可用反向代理

单台 Nginx 反向代理存在单点故障风险。生产环境通常配合 Keepalived 实现高可用。

# 两台 Nginx 节点运行相同配置
# 配合 Keepalived 提供虚拟 IP(VIP)漂移
# 见 [[63-高可用与集群]]

简要示例:

# Node 1(主)
sudo apt install keepalived -y
sudo vim /etc/keepalived/keepalived.conf
vrrp_instance VI_1 {
 state MASTER
 interface eth0
 virtual_router_id 51
 priority 100
 advert_int 1
 authentication {
 auth_type PASS
 auth_pass s3cr3t
 }
 virtual_ipaddress {
 192.168.1.100/24
 }
}
sudo systemctl enable --now keepalived
# DNS 将 api.example.com 指向 VIP 192.168.1.100

详见 63-高可用与集群


55.13 故障排查清单

症状可能原因排查命令
502 Bad Gateway后端服务未运行curl http://127.0.0.1:3000
504 Gateway Timeout后端响应过慢检查 proxy_read_timeout,查看后端负载
连接被拒绝后端端口未监听`ss -tlnp
部分后端不可用健康检查标记下线检查 Nginx error.log
WebSocket 断开未设置 Upgrade 头确认 proxy_set_header Upgrade
缓存不生效路径或状态码不匹配curl -I 查看 X-Cache-Status
SSL 证书问题证书过期或路径错误openssl x509 -in cert.crt -noout -dates
限流误伤正常用户burst 值太小增大 burst 或使用 $binary_remote_addr
# 快速诊断脚本
#!/bin/bash
echo "=== Nginx 状态 ==="
systemctl status nginx --no-pager | head -5
 
echo "=== 后端健康检查 ==="
for backend in 3000 3001 5000 5001 8080; do
 curl -s -o /dev/null -w "%{http_code}" http://127.0.0.1:$backend/health 2>/dev/null \
 && echo " :$backend OK" || echo " :$backend FAIL"
done
 
echo "=== 最近 Nginx 错误 ==="
tail -5 /var/log/nginx/error.log

55.14 本章总结

功能核心指令参考
反向代理proxy_pass本章 26-Web服务器(Nginx+Apache)
负载均衡upstream + server本章
健康检查max_fails / fail_timeout本章
SSL 终止ssl_certificate + listen 443 ssl本章
WebSocketproxy_set_header Upgrade本章
缓存proxy_cache_path / proxy_cache本章
限流limit_req_zone / limit_conn本章
高可用Keepalived VRRP63-高可用与集群
监控stub_status / ngxtop本章 60-监控系统(Prometheus+Grafana)