主题
06 · 负载均衡 · 把请求平均分给多台后端
生活类比:负载均衡就像银行多个柜台叫号——大堂经理(Nginx)看哪个柜台空着就把客户引过去,谁都不会一直等、谁都不会被累死。
1. 为啥要负载均衡?
单台后端的瓶颈:
┌─ 1000 QPS ─────► [挂了 ☠️]
浏览器 ───► └─ 后端单机
CPU 100% 内存爆 请求堆积加上负载均衡:
┌─► 后端 A (300 QPS)
浏览器 ──► Nginx ─┼─► 后端 B (300 QPS)
└─► 后端 C (400 QPS)
↑
加机器线性扩容!好处:扩容容易、故障容灾、单点不再致命。
2. Nginx 配置 · 一图记牢
nginx
# ① 定义后端组(upstream)
upstream backend {
server 10.0.0.1:8080;
server 10.0.0.2:8080;
server 10.0.0.3:8080;
}
# ② 反向代理过去
server {
listen 80;
location /api/ {
proxy_pass http://backend/;
}
}upstream 就是"后端服务器池",里面写几台后端就行了。proxy_pass 指向这个池的名字(不要写 IP)。
3. 5 大主流算法(重点)
3.1 轮询(默认)— Round Robin
nginx
upstream backend {
server 10.0.0.1;
server 10.0.0.2;
server 10.0.0.3;
}请求按顺序:A → B → C → A → B → C → …
生活类比:3 个柜台依次叫号,雨露均沾。 适合:所有后端机器配置相同的场景。
3.2 加权轮询 — Weighted Round Robin
nginx
upstream backend {
server 10.0.0.1 weight=5; # 5/8 流量
server 10.0.0.2 weight=2; # 2/8 流量
server 10.0.0.3 weight=1; # 1/8 流量
}8 个请求里 → A 拿 5 个、B 拿 2 个、C 拿 1 个。
生活类比:A 是 32 核大机器、B 是 16 核、C 是 8 核 —— 给 A 多分点活儿才公平。 适合:后端机器配置不一致。
3.3 IP 哈希 — IP Hash
nginx
upstream backend {
ip_hash;
server 10.0.0.1;
server 10.0.0.2;
server 10.0.0.3;
}按客户端 IP 算哈希,同一个 IP 永远落到同一台后端。
生活类比:每个 VIP 客户固定一个客服经理。 适合:登录态在内存(session)里、要求"会话粘性(sticky session)"的老系统。 不适合:移动用户 IP 切换、家庭出口 IP 共享。
⚠️ 现代做法:把 session 放到 Redis,用任何算法都行——别再依赖 ip_hash 了。
3.4 最少连接 — Least Connections
nginx
upstream backend {
least_conn;
server 10.0.0.1;
server 10.0.0.2;
server 10.0.0.3;
}把新请求发给"当前活跃连接数最少"的后端。
生活类比:哪个柜台前的队最短就排哪个。 适合:后端处理时长波动大的场景(有的请求 10ms,有的 5s)。
3.5 一致性哈希 — Consistent Hash(需第三方模块或商业版)
nginx
upstream backend {
hash $request_uri consistent; # 或 $http_x_user_id
server 10.0.0.1;
server 10.0.0.2;
}按指定的 key(URL、User-ID、设备号…)算哈希,同一个 key 永远落到同一台后端。比 ip_hash 更可控。
适合:缓存命中(同一个 URL 总打到同一台,提升缓存利用率)。
4. 算法对比表
| 算法 | 关键字 | 优点 | 缺点 | 典型场景 |
|---|---|---|---|---|
| 轮询 | (默认) | 简单、均匀 | 不适合机器异构 | 通用 |
| 加权轮询 | weight=N | 适应异构机器 | 静态分配 | 老机器+新机器混部 |
| IP 哈希 | ip_hash; | 会话粘性 | IP 切换会断、不均匀 | 老 PHP / Java 站 |
| 最少连接 | least_conn; | 自适应负载 | 实现稍复杂 | 处理时长不均 |
| 一致性哈希 | hash ... consistent | 缓存命中、节点变动影响小 | 需开源模块或 Plus 版 | 缓存层 / Redis 路由 |
5. 健康检查 · 自动剔除挂掉的后端
5.1 被动健康检查(Nginx 开源版自带)
nginx
upstream backend {
server 10.0.0.1 max_fails=3 fail_timeout=30s;
server 10.0.0.2 max_fails=3 fail_timeout=30s;
server 10.0.0.3 max_fails=3 fail_timeout=30s;
}| 参数 | 含义 |
|---|---|
max_fails=3 | 30 秒内失败 3 次 → 标记为不可用 |
fail_timeout=30s | 不可用持续 30 秒后再尝试 |
机制:在真实请求中失败才记次。没流量时不会主动探测——这是开源版的局限。
5.2 主动健康检查(Nginx Plus / Tengine / OpenResty 才有)
nginx
upstream backend {
server 10.0.0.1;
server 10.0.0.2;
health_check uri=/healthz interval=5s;
}定时去 /healthz 探一下,挂了立刻拉走。生产建议用 Tengine 或 APISIX 替代开源版 Nginx。
5.3 备用机器 · backup
nginx
upstream backend {
server 10.0.0.1;
server 10.0.0.2;
server 10.0.0.99 backup; # 只有上面全挂时才用
}6. 长连接到上游 · 大幅提速
nginx
upstream backend {
server 10.0.0.1;
server 10.0.0.2;
keepalive 64; # 每个 worker 与上游保持 64 条空闲长连接
}
server {
location /api/ {
proxy_pass http://backend/;
# 必须打开 HTTP/1.1 + 清空 Connection 头
proxy_http_version 1.1;
proxy_set_header Connection "";
}
}不开长连接,每次请求要重新 TCP 三次握手 → 后端 RT 平白多 1~30ms。开了之后能复用 → 少 30%+ 后端延迟。
7. 完整生产模板
nginx
upstream api_backend {
least_conn; # 算法
keepalive 64; # 上游长连接
server 10.0.0.1:8080 weight=2 max_fails=3 fail_timeout=30s;
server 10.0.0.2:8080 weight=2 max_fails=3 fail_timeout=30s;
server 10.0.0.3:8080 weight=1 max_fails=3 fail_timeout=30s;
server 10.0.0.99:8080 backup;
}
server {
listen 80;
server_name api.example.com;
location / {
proxy_pass http://api_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_connect_timeout 5s;
proxy_read_timeout 30s;
# 失败了重试别的后端
proxy_next_upstream error timeout http_502 http_503 http_504;
proxy_next_upstream_tries 2;
proxy_next_upstream_timeout 10s;
}
}proxy_next_upstream 是个救命指令:上游某台报 502 时,自动转给下一台重试,用户全程无感知。
8. ⚠️ 负载均衡常见坑
- upstream 名字与某 server 同名 → Nginx 启动报错。换个名(如
api_backend)。 - session 不共享导致用户老掉线 → 别用 ip_hash 苟着;上 Redis 共享 session。
- 后端某台假死 → 开源 Nginx 不会主动探测,需要 Tengine / APISIX 主动健康检查。
proxy_next_upstream把 POST 也重试了 → 接口不幂等的话会重复下单!加non_idempotent才会重试 POST,默认是不重试——但前端改 fetch 重试时要小心。- 后端长连接没启用 → 每次都要 TCP 握手,浪费几毫秒。
keepalive+proxy_http_version 1.1+Connection ""三件套。 - 加权值不科学 → 别凭感觉拍脑袋;按 CPU / 内存 / 历史 QPS 实测调。
9. 章末面试题速览
详见
qa.md第 16-19 题。
- Nginx 有哪些负载均衡算法? → 轮询 / 加权轮询 / ip_hash / least_conn / hash(一致性需 Plus)。
- ip_hash 解决会话粘性,还有什么问题? → 移动用户 IP 切换会丢 session;后端节点变动会大量重哈希。
- 怎么剔除挂掉的后端? → max_fails / fail_timeout(被动);Plus / Tengine 支持主动 health_check。
- 怎么让上游用长连接? → upstream 块加
keepalive N+ 反代里proxy_http_version 1.1; proxy_set_header Connection "";
10. 一句话总结
负载均衡 = upstream 列后端 + 选算法 + 加健康检查:默认轮询打天下、有粘性需求上 ip_hash 或 hash、有快慢请求差异用 least_conn。
下一章 → 07 · location 匹配规则:把 Nginx 最考验脑回路的 7 种匹配模式弄清楚。
🎬 可视化演示
下方 demo 让你直观看到:同样一批请求,不同算法(轮询 / 加权 / IP-hash / 最少连接)会怎么把请求分发到 4 台后端。
🎬 可视化演示
演示加载缓慢或样式异常?点此在新标签页打开 ↗