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

Nginx 是目前最流行的 Web 服务器之一,它的反向代理和负载均衡能力使其成为现代 Web 架构的核心组件。

基础反向代理

最简单的反向代理配置:

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

    location / {
        proxy_pass http://localhost: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 指令详解

proxy_pass

# 带路径替换
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

location / {
    proxy_pass http://backend;
    
    # 传递真实客户端 IP
    proxy_set_header X-Real-IP $remote_addr;
    proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
    
    # 传递原始 Host
    proxy_set_header Host $host;
    
    # 传递协议信息
    proxy_set_header X-Forwarded-Proto $scheme;
}

超时配置

location / {
    proxy_pass http://backend;
    
    # 连接超时
    proxy_connect_timeout 10s;
    
    # 发送超时
    proxy_send_timeout 30s;
    
    # 读取超时
    proxy_read_timeout 30s;
}

WebSocket 代理

location /ws/ {
    proxy_pass http://ws-backend;
    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;
    
    # WebSocket 需要更长的超时
    proxy_read_timeout 3600s;
}

负载均衡

Round Robin(默认)

upstream backend {
    server backend1.example.com;
    server backend2.example.com;
    server backend3.example.com;
}

server {
    listen 80;
    location / {
        proxy_pass http://backend;
    }
}

加权轮询

upstream backend {
    server backend1.example.com weight=3;
    server backend2.example.com weight=2;
    server backend3.example.com weight=1;
    # backend1 处理 50% 的请求
}

IP Hash(会话保持)

upstream backend {
    ip_hash;
    server backend1.example.com;
    server backend2.example.com;
    # 同一 IP 始终路由到同一服务器
}

Least Connections

upstream backend {
    least_conn;
    server backend1.example.com;
    server backend2.example.com;
    # 请求发送到活跃连接数最少的服务器
}

健康检查

upstream backend {
    server backend1.example.com max_fails=3 fail_timeout=30s;
    server backend2.example.com max_fails=3 fail_timeout=30s;
    server backend3.example.com backup;  # 备用服务器
    # 30 秒内失败 3 次则标记为不可用
}

缓存配置

proxy_cache_path /var/cache/nginx levels=1:2 keys_zone=my_cache:10m 
                  max_size=10g inactive=60m use_temp_path=off;

server {
    location /api/ {
        proxy_pass http://backend;
        
        proxy_cache my_cache;
        proxy_cache_key "$scheme$request_method$host$request_uri";
        proxy_cache_valid 200 10m;
        proxy_cache_valid 404 1m;
        proxy_cache_bypass $http_cache_control;
        
        # 缓存状态头
        add_header X-Cache-Status $upstream_cache_status;
    }
}

限流

# 定义限流区域
limit_req_zone $binary_remote_addr zone=api_limit:10m rate=10r/s;
limit_conn_zone $binary_remote_addr zone=conn_limit:10m;

server {
    location /api/ {
        # 请求速率限制:每秒 10 个,突发 20 个
        limit_req zone=api_limit burst=20 nodelay;
        
        # 并发连接数限制
        limit_conn conn_limit 10;
        
        proxy_pass http://backend;
    }
}

完整生产配置示例

upstream app_backend {
    least_conn;
    server 10.0.1.10:3000 weight=2 max_fails=3 fail_timeout=30s;
    server 10.0.1.11:3000 weight=2 max_fails=3 fail_timeout=30s;
    server 10.0.1.12:3000 weight=1 max_fails=3 fail_timeout=30s;
    keepalive 32;
}

server {
    listen 80;
    server_name example.com;
    return 301 https://$server_name$request_uri;
}

server {
    listen 443 ssl http2;
    server_name example.com;

    ssl_certificate /etc/letsencrypt/live/example.com/fullchain.pem;
    ssl_certificate_key /etc/letsencrypt/live/example.com/privkey.pem;
    ssl_protocols TLSv1.2 TLSv1.3;
    ssl_ciphers HIGH:!aNULL:!MD5;

    # 安全头
    add_header X-Frame-Options "SAMEORIGIN" always;
    add_header X-Content-Type-Options "nosniff" always;
    add_header X-XSS-Protection "1; mode=block" always;
    add_header Strict-Transport-Security "max-age=31536000" always;

    # Gzip
    gzip on;
    gzip_types text/plain text/css application/json application/javascript text/xml application/xml;

    # 静态文件
    location /_next/static/ {
        alias /app/.next/static/;
        expires 365d;
        add_header Cache-Control "public, immutable";
    }

    # API 代理
    location /api/ {
        proxy_pass http://app_backend;
        proxy_http_version 1.1;
        proxy_set_header Connection "";
        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 10s;
        proxy_read_timeout 30s;
    }

    # 前端应用
    location / {
        proxy_pass http://app_backend;
        proxy_http_version 1.1;
        proxy_set_header Upgrade $http_upgrade;
        proxy_set_header Connection "upgrade";
        proxy_set_header Host $host;
    }
}

调试技巧

# 自定义日志格式用于调试
log_format proxy_debug '$remote_addr - $remote_user [$time_local] '
                       '"$request" $status $body_bytes_sent '
                       '"$http_referer" "$http_user_agent" '
                       'upstream: $upstream_addr '
                       'upstream_status: $upstream_status '
                       'upstream_response_time: $upstream_response_time '
                       'request_time: $request_time';

# 验证配置
nginx -t

# 重新加载
nginx -s reload

# 查看 upstream 状态
curl http://localhost/nginx_status

总结

Nginx 的反向代理和负载均衡能力非常强大。掌握这些配置技巧,你就能构建高可用、高性能的 Web 服务架构。关键在于:

  • 正确设置 proxy headers
  • 选择合适的负载均衡策略
  • 配置缓存和限流保护后端
  • 做好健康检查和故障转移