Nginx 反向代理与负载均衡实战——从入门到生产环境

Nginx 是目前最流行的 Web 服务器和反向代理软件,以高性能、低资源消耗、高稳定性著称。本文将带你从零开始,系统学习 Nginx 反向代理与负载均衡的核心概念、配置方法和生产环境最佳实践。

一、反向代理基础

1.1 什么是反向代理?

反向代理(Reverse Proxy)是一种代理服务器,它接收客户端的请求,然后将请求转发给后端的真实服务器,最后将服务器的响应返回给客户端。

与正向代理(VPN、翻墙代理)不同,反向代理是为服务器服务的,客户端不知道真实服务器的存在,只与反向代理服务器通信。

1.2 反向代理的作用

  • 隐藏后端服务器:客户端只能看到反向代理服务器的 IP,提高安全性
  • 负载均衡:将请求分发到多个后端服务器,提高系统吞吐量和可用性
  • SSL 卸载:统一在反向代理层处理 HTTPS 加密解密,减轻后端服务器压力
  • 静态文件缓存:直接返回静态资源,减少后端请求
  • 统一入口:所有请求都经过反向代理,方便做访问控制、日志、限流等

1.3 Nginx vs Apache vs HAProxy

软件优势劣势适用场景
Nginx高性能、低内存、支持反向代理和负载均衡动态处理能力弱Web 服务器、反向代理、负载均衡
Apache模块丰富、对动态页面支持好内存占用高、并发性能一般传统 Web 应用、需要复杂模块
HAProxy专门的负载均衡器、性能极强不支持静态文件服务四层/七层负载均衡、高并发场景

对于绝大多数运维场景,Nginx 是最佳选择,它同时具备 Web 服务器、反向代理、负载均衡三种能力,配置简单,生态成熟。

二、基础反向代理配置

2.1 最简单的反向代理

server {
    listen 80;
    server_name app.yourdomain.com;

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

这就是最简单的反向代理配置,所有访问 app.yourdomain.com 的请求都会被转发到本地 8080 端口的服务。

2.2 关键代理参数

生产环境中,我们还需要配置一些关键参数来保证代理的正确性和性能:

server {
    listen 80;
    server_name app.yourdomain.com;

    location / {
        # 后端服务地址
        proxy_pass http://127.0.0.1:8080;
        
        # 设置请求头,让后端能获取到真实客户端信息
        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;
    }
}

参数说明:

  • proxy_set_header Host $host:保留原始请求的域名,否则后端拿到的是 127.0.0.1:8080
  • X-Real-IP:真实客户端 IP
  • X-Forwarded-For:请求经过的所有代理 IP 链
  • X-Forwarded-Proto:原始请求协议(HTTP/HTTPS)

2.3 路径重写

有时候需要对请求路径进行重写后再转发给后端:

location /api/ {
    # 将 /api/xxx 重写为 /xxx 后转发
    rewrite ^/api/(.*)$ /$1 break;
    proxy_pass http://127.0.0.1:8080;
}

或者使用 proxy_pass 的路径功能:

location /api/ {
    # 注意末尾的 /,这会将 /api/xxx 转发为 http://backend/xxx
    proxy_pass http://127.0.0.1:8080/;
}

三、负载均衡配置

3.1 基础负载均衡

当后端有多个服务实例时,我们可以用 upstream 配置负载均衡:

# 定义后端服务器组
upstream myapp {
    server 192.168.1.10:8080;
    server 192.168.1.11:8080;
    server 192.168.1.12:8080;
}

server {
    listen 80;
    server_name app.yourdomain.com;

    location / {
        proxy_pass http://myapp;
        proxy_set_header Host $host;
        proxy_set_header X-Real-IP $remote_addr;
        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
    }
}

默认使用轮询策略,每个请求按时间顺序逐一分配到不同的后端服务器。

3.2 负载均衡策略

Nginx 提供了多种负载均衡算法:

1. 轮询(默认)

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

2. 加权轮询
按权重分配请求,权重越高分配的请求越多:

upstream myapp {
    server 192.168.1.10:8080 weight=3;  # 3/5 的请求
    server 192.168.1.11:8080 weight=2;  # 2/5 的请求
}

3. IP 哈希(ip_hash)
按客户端 IP 分配,同一个客户端的请求始终转发到同一个后端服务器,解决 session 共享问题:

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

4. 最少连接(least_conn)
转发给当前连接数最少的后端服务器:

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

5. 最短响应时间(fair)
按后端响应时间分配,需要安装第三方模块:

upstream myapp {
    fair;
    server 192.168.1.10:8080;
    server 192.168.1.11:8080;
}

3.3 健康检查

Nginx 内置了被动健康检查机制,当某台后端服务器出现问题时,会自动将其从负载均衡池中临时剔除:

upstream myapp {
    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 backup;  # 备用服务器,只有其他都挂了才用
}

参数说明:

  • max_fails=3:允许 3 次请求失败
  • fail_timeout=30s:30 秒内失败 3 次就暂停该服务器 30 秒
  • backup:标记为备用服务器,只有其他所有服务器都不可用时才启用
  • down:标记服务器永久下线

3.4 长连接配置

Nginx 和后端服务器之间使用长连接可以减少 TCP 握手开销,显著提升性能:

upstream myapp {
    server 192.168.1.10:8080;
    server 192.168.1.11:8080;
    keepalive 64;  # 保持 64 个空闲长连接
}

server {
    location / {
        proxy_pass http://myapp;
        proxy_set_header Connection "";  # 清除客户端 Connection 头
        proxy_http_version 1.1;  # 使用 HTTP/1.1 支持长连接
    }
}

四、高级反向代理技巧

4.1 静态文件分离

将静态文件直接由 Nginx 返回,动态请求转发给后端,这是最常见的性能优化手段:

server {
    listen 80;
    server_name www.yourdomain.com;
    root /var/www/myapp;

    # 静态文件直接由 Nginx 返回
    location ~* \.(jpg|jpeg|png|gif|ico|css|js|woff|woff2)$ {
        expires 30d;  # 浏览器缓存 30 天
        add_header Cache-Control "public, immutable";
    }

    # 动态请求转发给后端
    location / {
        proxy_pass http://127.0.0.1:8080;
        proxy_set_header Host $host;
        proxy_set_header X-Real-IP $remote_addr;
    }
}

4.2 基于路径的多服务代理

同一个域名下不同路径转发给不同的后端服务:

server {
    listen 80;
    server_name www.yourdomain.com;

    location /api/ {
        proxy_pass http://api-service/;
    }

    location /admin/ {
        proxy_pass http://admin-service/;
    }

    location /static/ {
        alias /var/www/static/;
    }

    location / {
        proxy_pass http://frontend-service;
    }
}

4.3 基于域名的虚拟主机

同一个 Nginx 实例可以同时代理多个域名:

# 网站 A
server {
    listen 80;
    server_name www.site-a.com;
    location / {
        proxy_pass http://site-a-backend;
    }
}

# 网站 B
server {
    listen 80;
    server_name www.site-b.com;
    location / {
        proxy_pass http://site-b-backend;
    }
}

4.4 WebSocket 代理

代理 WebSocket 需要特殊配置,因为 WebSocket 使用 Upgrade 头:

location /ws/ {
    proxy_pass http://websocket-service;
    proxy_http_version 1.1;
    proxy_set_header Upgrade $http_upgrade;
    proxy_set_header Connection "upgrade";
    proxy_read_timeout 86400;  # WebSocket 连接超时设为 1 天
}

五、生产环境最佳实践

5.1 隐藏 Nginx 版本号

不要暴露具体的 Nginx 版本,减少被定向攻击的风险:

# 在 http 块中配置
http {
    server_tokens off;  # 隐藏版本号
    # ...
}

5.2 配置客户端上传大小限制

http {
    client_max_body_size 100m;  # 最大上传 100MB
    # ...
}

5.3 启用 Gzip 压缩

压缩响应内容,减少网络传输量,提升加载速度:

http {
    gzip on;
    gzip_vary on;
    gzip_min_length 1024;
    gzip_types text/plain text/css text/xml text/javascript application/json application/javascript application/xml+rss;
    # ...
}

5.4 配置访问日志

http {
    log_format main '$remote_addr - $remote_user [$time_local] "$request" '
                    '$status $body_bytes_sent "$http_referer" '
                    '"$http_user_agent" "$http_x_forwarded_for"';

    access_log /var/log/nginx/access.log main;
    error_log /var/log/nginx/error.log warn;
    # ...
}

5.5 限流配置

防止恶意请求刷爆你的服务器:

http {
    # 定义限流规则,按客户端 IP 限流,每秒最多 10 个请求
    limit_req_zone $binary_remote_addr zone=one:10m rate=10r/s;

    server {
        location / {
            # 应用限流规则,允许突发 5 个请求
            limit_req zone=one burst=5 nodelay;
            proxy_pass http://myapp;
        }
    }
}

六、常见问题排查

6.1 502 Bad Gateway

  • 后端服务挂了,检查后端服务是否正常运行
  • 后端服务端口变了,检查 proxy_pass 配置
  • 防火墙阻止了 Nginx 和后端的通信
  • 后端服务响应超时,调大 proxy_read_timeout

6.2 504 Gateway Timeout

  • 后端服务处理太慢,优化后端性能
  • 调大代理超时时间:proxy_read_timeout 60s;
  • 后端发生了死锁或死循环

6.3 获取不到真实客户端 IP

  • 检查是否配置了 proxy_set_header X-Real-IP $remote_addr;
  • 确认后端应用从 X-Real-IPX-Forwarded-For 头中获取 IP
  • 如果 Nginx 前面还有一层 CDN 或负载均衡,需要配置 real_ip_header

6.4 重定向地址变成内部地址

  • 配置 proxy_redirect off; 或者正确设置 proxy_set_header Host $host;
  • 后端服务生成绝对地址时使用了错误的 Host 头

七、Nginx 配置调试技巧

7.1 验证配置文件

nginx -t  # 只验证不重启
nginx -s reload  # 验证通过后平滑重启

7.2 查看 Nginx 版本和编译参数

nginx -V

7.3 启用 debug 日志

# 在 server 或 location 块中配置
error_log /var/log/nginx/debug.log debug;

7.4 返回调试信息

临时添加一个 location 返回 Nginx 变量值:

location /debug {
    return 200 "host=$host remote_addr=$remote_addr proxy_add_x_forwarded_for=$proxy_add_x_forwarded_for";
}

八、总结

Nginx 反向代理与负载均衡是现代 Web 架构的核心组件,掌握好它是每个运维工程师的必备技能。

核心要点回顾:

  1. 反向代理可以隐藏后端服务、统一入口、做 SSL 卸载和缓存
  2. proxy_pass 是最核心的代理指令,注意末尾 / 的区别
  3. 必须配置 proxy_set_header 传递真实客户端信息给后端
  4. upstream 支持轮询、加权、ip_hash、least_conn 等多种负载均衡策略
  5. 生产环境一定要配置健康检查、长连接、限流、日志

按照本文的配置方法,你可以快速搭建一套高性能、高可用的 Nginx 反向代理和负载均衡集群,支撑每秒数万甚至数十万的并发请求。