Skip to content

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=330 秒内失败 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. ⚠️ 负载均衡常见坑

  1. upstream 名字与某 server 同名 → Nginx 启动报错。换个名(如 api_backend)。
  2. session 不共享导致用户老掉线 → 别用 ip_hash 苟着;上 Redis 共享 session。
  3. 后端某台假死 → 开源 Nginx 不会主动探测,需要 Tengine / APISIX 主动健康检查。
  4. proxy_next_upstream 把 POST 也重试了 → 接口不幂等的话会重复下单!加 non_idempotent 才会重试 POST,默认是不重试——但前端改 fetch 重试时要小心。
  5. 后端长连接没启用 → 每次都要 TCP 握手,浪费几毫秒。keepalive + proxy_http_version 1.1 + Connection "" 三件套。
  6. 加权值不科学 → 别凭感觉拍脑袋;按 CPU / 内存 / 历史 QPS 实测调。

9. 章末面试题速览

详见 qa.md 第 16-19 题。

  1. Nginx 有哪些负载均衡算法? → 轮询 / 加权轮询 / ip_hash / least_conn / hash(一致性需 Plus)。
  2. ip_hash 解决会话粘性,还有什么问题? → 移动用户 IP 切换会丢 session;后端节点变动会大量重哈希。
  3. 怎么剔除挂掉的后端? → max_fails / fail_timeout(被动);Plus / Tengine 支持主动 health_check。
  4. 怎么让上游用长连接? → 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 台后端。

🎬 可视化演示

演示加载缓慢或样式异常?点此在新标签页打开 ↗