人人都会AI编程

Nginx 反向代理与负载均衡

更新时间:2026-07-10

在前面的小节中,我们提到利用 cluster 模块或 PM2 的集群模式可以在单台服务器上充分利用多核 CPU。但是,当流量继续增长,需要通过多台服务器来分担压力时,就需要在 Node.js 应用实例之前引入专门的负载均衡器。Nginx 是目前使用最广泛的反向代理和负载均衡服务器,它性能极高、配置灵活,还能承担静态资源服务、HTTPS 终止、缓存等职责。

为什么需要 Nginx 在前置

Node.js 进程本身可以直接监听 80 或 443 端口对外提供服务,但引入 Nginx 之后可以获得几个关键的增强能力:

  • 负载均衡:将流量按照一定策略分发给后端的多个 Node.js 实例,可以是同一台机器的不同进程,也可以是多台不同服务器。
  • 零宕机部署:重启某一个 Node.js 实例时,其他实例仍可继续服务,Nginx 能够自动摘除故障节点,保证服务整体可用。
  • 静态资源加速:Nginx 处理静态文件的能力远优于 Node.js,可以直接将由 Express/Koa 等服务传递过来的请求中静态资源分离出来,降低 Node 进程的负载。
  • HTTPS 卸载:在 Nginx 上统一配置 SSL 证书并终止 HTTPS,后端的 Node.js 实例只需处理 HTTP 请求,简化证书管理和应用更新。
  • 请求缓存与压缩:Nginx 可以缓存反向代理的响应,对返回的内容进行 Gzip 压缩,进一步优化传输效率。

配置一个基本的反向代理

假设我们有两个 Node.js 应用实例分别运行在本地 3001 和 3002 端口,现在希望用 Nginx 监听 80 端口并将请求轮询分发到这两个实例。

首先,在 /etc/nginx/conf.d/node_app.conf 或站点配置文件中添加:

upstream node_backend {
    server 127.0.0.1:3001;
    server 127.0.0.1:3002;
}

server {
    listen 80;
    server_name example.com;

    # 日志记录
    access_log /var/log/nginx/node_app_access.log;
    error_log  /var/log/nginx/node_app_error.log;

    # 静态资源直接由 Nginx 处理
    location /static/ {
        root /var/www/node_app/public;
        expires 30d;
        add_header Cache-Control "public, immutable";
    }

    # 其他请求转发到 Node 集群
    location / {
        proxy_pass http://node_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;
        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
        proxy_set_header X-Forwarded-Proto $scheme;
        proxy_cache_bypass $http_upgrade;
    }
}

这一配置的核心点:

  • upstream 块定义了一组后端服务器,Nginx 会在其中进行负载均衡。
  • proxy_pass http://node_backend 将请求转发给这个上游组。
  • proxy_set_header 保证了真实客户端 IP、协议等信息能传递到 Node.js 应用,这在读取 req.ip 或日志记录时很重要。
  • /static/ 路径直接由 Nginx 读取本地磁盘文件,并设置了长期的缓存头,不再拖累 Node 进程。

负载均衡策略

Nginx 的 upstream 支持多种负载均衡算法,可以根据业务需要选择:

轮询(默认)

upstream node_backend {
    server 127.0.0.1:3001;
    server 127.0.0.1:3002;
}

请求依次轮流分配到每个服务器,如果后端实例性能相近,这是最简单的方案。

权重轮询

upstream node_backend {
    server 127.0.0.1:3001 weight=3;
    server 127.0.0.1:3002 weight=1;
}

在服务器配置不均衡(如一台机器比另一台强)时,通过 weight 让高性能实例承担更多请求。

最少连接(least_conn)

upstream node_backend {
    least_conn;
    server 127.0.0.1:3001;
    server 127.0.0.1:3002;
}

将请求分配到当前活跃连接数最少的服务器,对于长连接类应用(如 WebSocket)效果较好,能更真实地反映负载情况。

IP Hash

upstream node_backend {
    ip_hash;
    server 127.0.0.1:3001;
    server 127.0.0.1:3002;
}

根据客户端 IP 计算哈希值,使得同一客户端的请求始终落到同一台后端,用于解决 Session 黏连问题。但在现代分布式系统中更推荐使用外部 Session 存储(Redis)来避免服务器绑定。

支持 WebSocket 反向代理

Node.js 应用常涉及 WebSocket 实时通信(如 Socket.IO),Nginx 需要正确升级连接协议。上文的配置中已经包含了关键头部:

proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection 'upgrade';

这三行确保了 WebSocket 握手时 UpgradeConnection 头部能被传递到后端,从而成功建立长连接。如果缺少这些配置,WebSocket 连接会被降级为普通的 HTTP 长轮询,极大降低性能。

健康检查与故障转移

Nginx 默认提供简单的被动健康检查:如果向某台服务器转发请求失败(如连接拒绝或超时),该服务器将被标记为不可用,并在一定时间后重试。可以通过以下参数调整行为:

upstream node_backend {
    server 127.0.0.1:3001 max_fails=3 fail_timeout=30s;
    server 127.0.0.1:3002 max_fails=3 fail_timeout=30s;
    server 127.0.0.1:3003 backup;
}
  • max_fails 等于 3 表示在 fail_timeout 时间内出现 3 次失败后暂时标记为不可用。
  • backup 表示只有当所有主服务器都不可用时,请求才会转发到这台备份服务器。

对于高可用要求更严的场景,可以结合 nginx_upstream_check_module 插件实现主动健康检查(对后端发送周期性探测请求),但大部分中小规模项目使用默认的被动检查已足够。

与 PM2 集群模式的配合

当使用 PM2 的集群模式启动多个 Node.js 实例时,每个实例会占用一个独立端口(或通过 cluster 共享端口)。如果 Nginx 负责对外服务,我们通常会让 PM2 的每个实例监听不同的端口,例如 3001、3002…,然后在 Nginx upstream 中一一列出。生产环境中,建议将 PM2 的实例绑定到本地回环地址(127.0.0.1),避免暴露在公网。

一个自动化管理的方法是用服务发现工具动态更新 Nginx 的 upstream 配置,但对于静态实例数量,手动维护配置已经足够稳定。

生产配置建议

真实项目中部署 Nginx 时还需要注意以下几点:

  • 使用 proxy_set_header 传递原始客户端信息:Node.js 的 Trust Proxy 设置需要开启(如在 Express 中使用 app.set('trust proxy', true)),才能正确识别从 Nginx 传来的 X-Forwarded-* 头部。
  • 开启 Gzip 压缩:在 Nginx 上全局开启 Gzip,可以将文本型响应(JSON、HTML)体积压缩到原来的 1/3,节省带宽并加快传输。
  • 限制请求速率:利用 limit_reqlimit_conn 模块保护后端 Node 进程,避免单 IP 恶意请求打满资源。
  • 长连接优化:在 proxy_pass 时调整 proxy_read_timeoutproxy_send_timeout 以适应业务最长的请求时间,避免正常慢请求被误杀。
  • 日志轮转:配置 access_log 的滚动策略,防止日志文件撑满磁盘。

示例:一个完整的 HTTPS 负载均衡配置

假定你已经拥有了 SSL 证书,以下配置展示了如何同时启用 HTTPS、静态资源服务和 WebSocket 代理:

upstream node_app {
    least_conn;
    server 127.0.0.1:3001 weight=2;
    server 127.0.0.1:3002 weight=1;
    server 127.0.0.1:3003 backup;
}

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

    ssl_certificate     /etc/ssl/certs/example.com.pem;
    ssl_certificate_key /etc/ssl/private/example.com.key;
    ssl_protocols       TLSv1.2 TLSv1.3;
    ssl_ciphers         HIGH:!aNULL:!MD5;

    # 静态文件
    location /static/ {
        root /var/www/app/public;
        expires 7d;
    }

    # API 接口和 WebSocket
    location / {
        proxy_pass http://node_app;
        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_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
        proxy_set_header X-Forwarded-Proto $scheme;
        proxy_read_timeout 60s;
    }
}

# HTTP 自动跳转到 HTTPS
server {
    listen 80;
    server_name example.com;
    return 301 https://$host$request_uri;
}

当你在多台服务器上进行水平扩展时,可以将 upstream 中的 IP 地址替换为其他机器的内网地址,Nginx 就会将请求均衡分发到整个 Node.js 集群上。

小结

Nginx 反向代理与负载均衡是 Node.js 生产环境高可用架构中不可或缺的一环。它充当了流量的“守门人”,将客户端的海量连接简单高效地分发到多个 Node.js 进程,同时卸下了 HTTPS、静态资源、压缩等额外负担。结合 PM2 或 cluster,开发者可以搭建出一个轻量但健壮的 Node.js 服务集群,足以应对绝大多数互联网应用的性能要求。