Nginx 反向代理与负载均衡完全指南

· 阅读约需34分钟

前言

在现代 Web 架构中,反向代理和负载均衡是构建高可用、高性能服务的核心组件。Nginx 作为一款轻量级、高性能的 HTTP 服务器和反向代理服务器,凭借其出色的并发处理能力和低资源消耗,成为了业界的首选方案。

本文将从基础概念入手,逐步深入到实战配置、负载均衡策略、性能优化等方面,帮助你全面掌握 Nginx 反向代理与负载均衡的核心技术。


一、什么是反向代理

1.1 正向代理 vs 反向代理

正向代理:代理客户端访问外部服务,客户端知道自己在使用代理,服务器不知道真实客户端是谁。典型应用:翻墙、公司内网访问外网。

反向代理:代理服务器接收客户端请求,转发给后端真实服务器,客户端不知道真实服务器是谁。典型应用:负载均衡、缓存加速、SSL 卸载。

客户端 → 反向代理服务器 → 后端真实服务器集群

1.2 反向代理的作用

  • 负载均衡:将请求分发到多台后端服务器,提高系统吞吐量和可用性
  • 缓存加速:缓存静态资源和动态内容,减少后端服务器压力
  • SSL 卸载:统一处理 HTTPS 加密解密,降低后端服务器开销
  • 安全防护:隐藏后端服务器真实 IP,提供 WAF、限流等安全功能
  • 统一入口:通过域名路径路由到不同后端服务,实现服务聚合

二、Nginx 反向代理基础配置

2.1 最简单的反向代理

server {
    listen 80;
    server_name example.com;

    location / {
        proxy_pass http://127.0.0.1:8080;
    }
}

这是最基础的配置,将所有请求转发到本地的 8080 端口。

2.2 常用代理参数

location / {
    proxy_pass http://backend;

    # 设置请求头
    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 30s;
    proxy_send_timeout 30s;
    proxy_read_timeout 30s;

    # 缓冲区设置
    proxy_buffering on;
    proxy_buffer_size 4k;
    proxy_buffers 8 4k;
    proxy_busy_buffers_size 8k;
}

2.3 路径路由配置

通过不同路径转发到不同后端服务:

server {
    listen 80;
    server_name example.com;

    # 静态资源由本地处理
    location /static/ {
        alias /var/www/static/;
        expires 30d;
    }

    # API 请求转发到后端服务
    location /api/ {
        proxy_pass http://api-server:8080/;
        proxy_set_header Host $host;
        proxy_set_header X-Real-IP $remote_addr;
    }

    # 管理后台转发到另一台服务器
    location /admin/ {
        proxy_pass http://admin-server:8081/;
        proxy_set_header Host $host;
    }

    # 其他请求转发到主站
    location / {
        proxy_pass http://web-server:8080;
        proxy_set_header Host $host;
    }
}

注意proxy_pass 末尾的斜杠很重要。加斜杠会把 location 匹配的部分去掉后再转发,不加斜杠则会把完整路径转发过去。


三、负载均衡策略详解

3.1 上游服务器配置

使用 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 example.com;

    location / {
        proxy_pass http://backend_servers;
        proxy_set_header Host $host;
    }
}

3.2 五种负载均衡策略

1. 轮询(默认)

请求按顺序逐一分配到不同后端服务器,是默认策略。

upstream backend {
    server 192.168.1.10:8080;
    server 192.168.1.11:8080;
}

2. 加权轮询(weight)

根据权重分配请求,权重越高分配的请求越多。

upstream backend {
    server 192.168.1.10:8080 weight=3;  # 3/4 的请求
    server 192.168.1.11:8080 weight=1;  # 1/4 的请求
}

适用场景:后端服务器性能差异较大时。

3. IP 哈希(ip_hash)

根据客户端 IP 地址的哈希结果分配服务器,同一客户端的请求始终落到同一台服务器上。

upstream backend {
    ip_hash;
    server 192.168.1.10:8080;
    server 192.168.1.11:8080;
}

适用场景:需要会话保持(Session Sticky)的场景。

缺点:如果客户端在 NAT 后面,所有用户会被分配到同一台服务器,导致负载不均。

4. 最少连接(least_conn)

将请求分配给当前连接数最少的后端服务器。

upstream backend {
    least_conn;
    server 192.168.1.10:8080;
    server 192.168.1.11:8080;
}

适用场景:请求处理时间差异较大的场景。

5. 最短响应时间(least_time) – Nginx Plus 功能

根据平均响应时间和连接数来分配请求,需要 Nginx Plus 商业版支持。

3.3 服务器状态参数

upstream backend {
    server 192.168.1.10:8080 weight=5 max_fails=3 fail_timeout=30s;
    server 192.168.1.11:8080 backup;
    server 192.168.1.12:8080 down;
}
  • weight:权重,默认 1
  • max_fails:最大失败次数,超过后标记为不可用
  • fail_timeout:失败超时时间,标记不可用后多久重试
  • backup:备份服务器,只有所有主服务器都不可用时才启用
  • down:标记服务器永久不可用

四、高级配置与优化

4.1 健康检查

Nginx 原生支持被动健康检查,通过 max_failsfail_timeout 实现。

如果需要主动健康检查(定期探测后端健康状态),有两种方案:

方案一:使用 Nginx Plus 商业版

upstream backend {
    zone backend 64k;
    server 192.168.1.10:8080;
    server 192.168.1.11:8080;
}

server {
    location / {
        proxy_pass http://backend;
        health_check interval=5s fails=3 passes=2;
    }
}

方案二:使用 nginx_upstream_check_module 第三方模块

需要编译安装,配置示例:

upstream backend {
    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 "GET /health HTTP/1.0\r\n\r\n";
    check_http_expect_alive http_2xx http_3xx;
}

4.2 缓存配置

# 在 http 块中定义缓存路径
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 / {
        proxy_pass http://backend;
        proxy_cache my_cache;
        proxy_cache_valid 200 302 10m;  # 200 和 302 响应缓存 10 分钟
        proxy_cache_valid 404 1m;       # 404 响应缓存 1 分钟
        proxy_cache_valid any 1m;       # 其他响应缓存 1 分钟

        # 缓存键
        proxy_cache_key $scheme$request_method$host$request_uri;

        # 添加缓存状态头,方便调试
        add_header X-Cache-Status $upstream_cache_status;
    }
}

4.3 SSL 卸载

将 HTTPS 加密解密工作放在 Nginx 层处理,后端服务器只需要处理 HTTP:

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

    # SSL 证书配置
    ssl_certificate /etc/nginx/ssl/example.com.crt;
    ssl_certificate_key /etc/nginx/ssl/example.com.key;

    # SSL 优化
    ssl_protocols TLSv1.2 TLSv1.3;
    ssl_ciphers ECDHE-RSA-AES256-GCM-SHA512:DHE-RSA-AES256-GCM-SHA512;
    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-Real-IP $remote_addr;
        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
        proxy_set_header X-Forwarded-Proto https;  # 告诉后端这是 HTTPS 请求
    }
}

# HTTP 重定向到 HTTPS
server {
    listen 80;
    server_name example.com;
    return 301 https://$server_name$request_uri;
}

4.4 限流与防刷

# 定义限流区域(在 http 块中)
limit_req_zone $binary_remote_addr zone=req_limit:10m rate=10r/s;
limit_conn_zone $binary_remote_addr zone=conn_limit:10m;

server {
    location /api/ {
        # 请求限流:每秒最多 10 个请求,突发 20 个
        limit_req zone=req_limit burst=20 nodelay;

        # 连接数限制:每个 IP 最多 10 个连接
        limit_conn conn_limit 10;

        proxy_pass http://api_backend;
    }
}

五、性能优化建议

5.1 系统层面优化

文件描述符限制

# 临时生效
ulimit -n 65535

# 永久生效,修改 /etc/security/limits.conf
* soft nofile 65535
* hard nofile 65535

内核参数优化(/etc/sysctl.conf):

# 优化 TCP 连接
net.ipv4.tcp_syncookies = 1
net.ipv4.tcp_tw_reuse = 1
net.ipv4.tcp_tw_recycle = 0  # 注意:NAT 环境下不要开启
net.ipv4.tcp_fin_timeout = 30
net.ipv4.tcp_keepalive_time = 600
net.ipv4.tcp_max_syn_backlog = 8192

# 优化端口范围
net.ipv4.ip_local_port_range = 1024 65535

# 优化缓冲区
net.core.rmem_max = 16777216
net.core.wmem_max = 16777216
net.ipv4.tcp_rmem = 4096 87380 16777216
net.ipv4.tcp_wmem = 4096 65536 16777216

5.2 Nginx 配置优化

# worker 进程数,建议设置为 CPU 核心数
worker_processes auto;

# 每个 worker 进程的最大连接数
events {
    worker_connections 65535;
    use epoll;
    multi_accept on;
}

http {
    # 高效文件传输
    sendfile on;
    tcp_nopush on;
    tcp_nodelay on;

    # 长连接超时
    keepalive_timeout 65;
    keepalive_requests 100;

    # 压缩
    gzip on;
    gzip_vary on;
    gzip_min_length 1k;
    gzip_comp_level 6;
    gzip_types text/plain text/css text/xml application/json application/javascript application/xml+rss;

    # 隐藏版本号
    server_tokens off;
}

5.3 上游连接优化

upstream backend {
    server 192.168.1.10:8080;
    server 192.168.1.11:8080;

    # 长连接配置
    keepalive 32;
    keepalive_timeout 60s;
    keepalive_requests 100;
}

server {
    location / {
        proxy_pass http://backend;
        proxy_http_version 1.1;
        proxy_set_header Connection "";  # 清除 Connection 头,支持长连接
    }
}

六、常见问题与排错

6.1 502 Bad Gateway

常见原因

  • 后端服务未启动或端口不对
  • 后端服务响应超时
  • 后端服务崩溃

排查步骤

  1. 检查后端服务是否正常运行:curl http://backend-ip:port
  2. 查看 Nginx 错误日志:tail -f /var/log/nginx/error.log
  3. 调整超时时间:proxy_read_timeout

6.2 504 Gateway Timeout

原因:后端处理时间过长,超过了 Nginx 的等待时间。

解决

proxy_read_timeout 300s;  # 增加读取超时
proxy_send_timeout 300s;  # 增加发送超时

6.3 获取真实客户端 IP

后端服务器看到的是 Nginx 的 IP,需要通过请求头传递真实 IP:

proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;

后端应用需要配置读取这些请求头。以 PHP 为例:

// 获取真实 IP
$real_ip = $_SERVER['HTTP_X_REAL_IP'] ?? $_SERVER['REMOTE_ADDR'];

6.4 静态资源 404

检查 proxy_pass 的路径是否正确,注意末尾斜杠的区别:

# 不加斜杠:请求 /api/user 会转发为 http://backend/api/user
location /api/ {
    proxy_pass http://backend;
}

# 加斜杠:请求 /api/user 会转发为 http://backend/user
location /api/ {
    proxy_pass http://backend/;
}

七、完整配置示例

下面是一个生产环境可用的完整配置示例:

# 上游服务器组
upstream app_backend {
    least_conn;
    server 192.168.1.10:8080 weight=3 max_fails=3 fail_timeout=30s;
    server 192.168.1.11:8080 weight=2 max_fails=3 fail_timeout=30s;
    server 192.168.1.12:8080 weight=1 max_fails=3 fail_timeout=30s;
    server 192.168.1.13:8080 backup;

    keepalive 64;
}

# 缓存配置
proxy_cache_path /var/cache/nginx levels=1:2 keys_zone=static_cache:100m max_size=10g inactive=7d use_temp_path=off;

# 限流配置
limit_req_zone $binary_remote_addr zone=api_limit:10m rate=20r/s;

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

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

    # SSL 配置
    ssl_certificate /etc/nginx/ssl/example.com.crt;
    ssl_certificate_key /etc/nginx/ssl/example.com.key;
    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 1d;

    # 静态资源缓存
    location ~* \.(jpg|jpeg|png|gif|ico|css|js|woff2?)$ {
        proxy_pass http://app_backend;
        proxy_cache static_cache;
        proxy_cache_valid 200 30d;
        proxy_cache_valid 404 1h;
        expires 30d;
        add_header Cache-Control "public, immutable";
    }

    # API 接口
    location /api/ {
        limit_req zone=api_limit burst=40 nodelay;

        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_read_timeout 60s;
        proxy_send_timeout 60s;
    }

    # 其他请求
    location / {
        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;
    }
}

总结

Nginx 反向代理和负载均衡是构建高可用 Web 架构的基础。通过本文的介绍,你应该能够:

  1. 理解反向代理的概念和作用
  2. 掌握 Nginx 反向代理的基础配置
  3. 灵活运用五种负载均衡策略
  4. 配置缓存、SSL 卸载、限流等高级功能
  5. 进行系统和 Nginx 层面的性能优化
  6. 排查常见的 502、504 等问题

在实际生产环境中,建议根据业务特点选择合适的负载均衡策略,并结合监控系统实时观察后端服务器的负载情况,不断调优配置参数。


参考资料