为什么要用Nginx做反向代理和负载均衡

如果你只是用单一服务器跑个Demo项目,那确实不需要折腾这些。但一旦你面对的是真实用户,高并发、单点故障、SSL证书管理、静态资源分离这些坑就会一个个冒出来。Nginx之所以流行,就是因为它把这些问题解决得足够优雅——高性能、低内存占用、配置灵活,堪称反向代理界的瑞士军刀。

其实,反向代理就是让Nginx替后端服务器“接客”,对外暴露一个统一入口。负载均衡则是把流量分散到多台后端服务器上,防止某一台被压垮。下面我会用一套完整的Node.js + Nginx配置带你走一遍,所有代码都能直接跑起来。

环境准备与基础配置

假设你手头有3台Node.js服务(或者同一台机器启动三个不同端口),我们用Nginx给它们做负载均衡。先准备后端服务:

// app.js — 一个简单的Node.js HTTP server,返回当前服务端口 const http = require('http'); const port = process.argv[2] || 3000; http.createServer((req, res) => { res.writeHead(200, { 'Content-Type': 'text/plain' }); res.end(`Hello from server on port ${port}\n`); }).listen(port, () => { console.log(`Server running on port ${port}`); });

开三个终端分别启动:

然后安装Nginx(以Ubuntu为例):

sudo apt update sudo apt install nginx

安装后默认会启动,访问 http://localhost 能看到欢迎页。接下来我们要改配置。

核心配置:反向代理 + 负载均衡

Nginx的配置通常写在 /etc/nginx/nginx.conf 或者 conf.d/ 目录下。为了方便调试,我建议直接修改 /etc/nginx/conf.d/myapp.conf(如果不存在就新建一个)。

下面是一个完整的、可直接运行的配置:

# 定义一个upstream后端服务器组 upstream backend { # 默认负载均衡算法:轮询(weight权重、ip_hash等可改) server 127.0.0.1:3001 weight=1; server 127.0.0.1:3002 weight=2; server 127.0.0.1:3003 weight=1; # 健康检查(Nginx Plus支持,但开源版可用其他方式) # 这里我们配置超时和重试 keepalive 32; # 连接池保持32个长连接 } server { listen 80; server_name localhost; # 生产环境改成你的域名 # 将所有请求代理到upstream组 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 3s; proxy_read_timeout 5s; proxy_send_timeout 5s; } # 静态文件直接由Nginx处理,不经过后端(反向代理的典型优化) location /static/ { root /var/www/static; expires 30d; add_header Cache-Control "public, immutable"; } # 拒绝访问 . 开头的隐藏文件 location ~ /\. { deny all; } }

保存配置后,先测试语法:

sudo nginx -t

如果输出 syntax is oktest is successful,再重载:

sudo systemctl reload nginx

现在打开浏览器访问 http://localhost,每次刷新你会看到来自不同端口的响应(3001、3002、3003),权重不同,3002出现的频率更高。

值得留意的是,proxy_set_header 这几行不能少,否则后端拿不到真实客户端IP,日志里全是127.0.0.1,排查问题时绝对会懵逼。

经验之谈实录:两个常见报错及解决方法

错误1:413 Request Entity Too Large

现象:上传文件时Nginx直接返回413,后端日志没有任何请求记录。

原因:Nginx默认限制请求体大小为1MB,超过就拒绝。这个坑我当年在给客户做文件上传系统时踩过,用户上传几十MB的PDF直接白屏。

解决方案:在 server 块或 location 块增加:

client_max_body_size 50m; # 根据实际需求调整

插到配置里对应位置后重载即可。

错误2:upstream timed out (110: Connection timed out)

现象:部分请求响应慢,Nginx返回504 Gateway Time-out,后端日志显示正常处理但耗时较长。

原因:后端处理请求超过Nginx设置的 proxy_read_timeout,Nginx主动断开连接。比如你有一个数据库查询需要10秒,而 proxy_read_timeout 设成了5秒。

解决方案:区分业务接口,对于慢接口可以单独配置更长超时:

location /slow-api/ { proxy_pass http://backend; proxy_read_timeout 60s; # 其他proxy_set_header照抄 }

另外别忘了增加 proxy_connect_timeout 防止后端启动慢导致连接超时。

我的实战经验与避雷经验心得

1. 永远不要在配置里硬编码IP

有些同学图省事,直接在 proxy_pass 里写 http://127.0.0.1:3001,但后期加机器时改起来麻烦。用 upstream 抽象一层,加机器只需要增加一行 server,而且可以在不停机的情况下修改配置(reload)。

2. 负载均衡算法别只盯着轮询

默认的轮询(round-robin)最简单,但不适用于有状态的应用。比如WebSocket、Session存储在内存里的单体应用,同一个用户的请求必须落到同一台机器。这时改用 ip_hash

upstream backend { ip_hash; server 127.0.0.1:3001; server 127.0.0.1:3002; server 127.0.0.1:3003; }

注意:如果上游服务器宕机,ip_hash会导致该IP的请求全部失败,最好配合健康检查机制。

3. 反向代理的缓存是个双刃剑

Nginx可以缓存后端响应,但我建议只在接口幂等且不频繁变化时才启用。有个血泪教训:我为某个API加了 proxy_cache,结果用户修改了数据后依然看到旧缓存,排查了半天才发现是缓存没清。如果你要用,务必设置合理的缓存key和过期策略。

4. 日志是排查问题的第一把钥匙

默认的access.log格式太简略,我会自定义日志格式,包含upstream地址和响应时间:

http { log_format detailed '$remote_addr - $remote_user [$time_local] ' '"$request" $status $body_bytes_sent ' '"$http_referer" "$http_user_agent" ' '"$upstream_addr" "$upstream_response_time"'; access_log /var/log/nginx/access.log detailed; }

这样一旦哪个上游服务器响应慢,直接在日志里就能看到 upstream_addrupstream_response_time,不用再去猜。

5. 蓝绿部署和灰度发布

传统做法是停服更新,但用Nginx可以做得很优雅。举个例子,你有两组服务器:backend_v1backend_v2。修改upstream,把所有流量切到v2,观察无异常后下线v1。或者按权重分配1%的流量到新版本做灰度测试:

upstream backend { server 127.0.0.1:3001 weight=99; # 老版本 server 127.0.0.1:3002 weight=1; # 新版本 }

这一步在生产环境极其有用,每次上线我都是这样干的。

最佳实践建议

安全性

location /admin { allow 192.168.1.0/24; deny all; proxy_pass http://backend; }

性能调优

gzip on; gzip_types text/plain text/css application/json application/javascript text/xml application/xml application/xml+rss text/javascript; gzip_min_length 1000;
sendfile on; tcp_nopush on;

监控与告警

建议给Nginx添加stub_status模块(一般自带),暴露简单的指标:

location /nginx_status { stub_status on; access_log off; allow 127.0.0.1; deny all; }

然后用Prometheus抓取或者自己写个脚本监控active connections等指标。

总结?不,没有总结

技术文章写到“总结”往往意味着开始注水。我只想说,Nginx的反向代理和负载均衡配置并不复杂,但真正用好需要你理解背后的网络交互、超时模型、连接池机制。上面给的都是可以立刻运行的代码,建议你打开编辑器照着敲一遍,遇到问题翻翻错误日志,99%的问题都能解决。记住:别怕改配置,反正有nginx -t兜底。

希望这些经验能让你少走我走过的弯路。

学习建议:Nginx配置不难,难的是理解背后的网络原理。建议先在本机用Docker搞两个简单的HTTP服务,配合Nginx反代调通,再逐步加上负载均衡、SSL、限流等高级功能。遇到502/504错误别慌,先看 error.log,99%的问题都在日志里。