Chapter 06

负载均衡

把流量均匀分给多台后端,是横向扩容的前提。理解各种均衡策略、失败剔除、会话保持与长连接,才能让集群既均衡又可靠。

6.1 核心概念

负载均衡(Load Balancing)
把大量请求按某种规则分摊到多台后端服务器,避免单机过载,同时实现横向扩容与容错。
轮询(Round Robin)
Nginx 默认策略,请求依次分给每台后端,循环往复。简单公平,适合后端配置一致的场景。
加权轮询(Weighted RR)
给每台后端设 weight,权重越高分到的请求越多。用于后端配置不均(如新机器更强)时按能力分配。
ip_hash
按客户端 IP 做哈希,同一 IP 总落到同一后端。用于需要"会话粘性"的场景(如本地 session)。
least_conn
把请求发给当前活跃连接数最少的后端。适合请求处理时长差异大的场景,比轮询更能防止某台堆积。
被动健康检查(max_fails / fail_timeout)
Nginx 开源版靠转发失败次数判断后端是否"不可用",达到阈值就在一段时间内不再向它派发请求。

6.2 默认轮询与加权

upstream backend {
    server  10.0.0.11:8080;
    server  10.0.0.12:8080;
    server  10.0.0.13:8080;
}

# 加权:性能强的机器多分流量
upstream weighted {
    server  10.0.0.11:8080  weight=5;   # 5/8 的请求
    server  10.0.0.12:8080  weight=2;
    server  10.0.0.13:8080  weight=1;
}

6.3 ip_hash 与 least_conn

# 同一客户端 IP 固定落一台(会话粘性)
upstream sticky {
    ip_hash;
    server  10.0.0.11:8080;
    server  10.0.0.12:8080;
}

# 发给连接数最少的后端(长请求友好)
upstream leastconn {
    least_conn;
    server  10.0.0.11:8080;
    server  10.0.0.12:8080;
}
ip_hash 不是理想的会话保持

大量用户共用一个出口 NAT IP(如整个公司/校园)时,ip_hash 会把他们全压到同一台。更干净的做法是让 session 无状态(存 Redis / JWT),后端就能随意轮询。会话保持是"没办法时的办法",不是首选架构。

6.4 健康检查与失败剔除

upstream backend {
    # 20 秒内失败 3 次,则标记该后端 down 20 秒
    server  10.0.0.11:8080  max_fails=3  fail_timeout=20s;
    server  10.0.0.12:8080  max_fails=3  fail_timeout=20s;

    # 备用机:只有主力全挂了才启用
    server  10.0.0.99:8080  backup;

    # 临时下线一台(发布时)
    server  10.0.0.13:8080  down;
}
backup
标记为备用后端,仅当所有非 backup 后端都不可用时才接收流量。用作冗余兜底。
down
手动标记该后端永久下线,Nginx 不再向它转发。发布/维护单台时临时加上。
主动健康检查在商业版

开源 Nginx 只有被动健康检查(靠真实请求失败来发现问题)。主动健康检查(定期主动探测 /healthz)是 Nginx Plus 商业特性,或用 nginx_upstream_check_module 第三方模块。K8s 场景则由 Ingress + readiness probe 承担。

6.5 失败重试

location / {
    proxy_pass  http://backend;

    # 遇到这些情况时,换一台后端重试
    proxy_next_upstream  error timeout http_502 http_503 http_504;
    proxy_next_upstream_tries  2;      # 最多再试 2 台
    proxy_next_upstream_timeout  10s;  # 重试总耗时上限
}
非幂等请求慎重试

POST/PUT 这类可能产生副作用的请求,自动重试可能导致"下两次单"。默认 proxy_next_upstream 不会对非幂等请求重试(除非显式加 non_idempotent)。转账、下单等接口切勿盲目开启重试。

6.6 keepalive 长连接

upstream backend {
    server  10.0.0.11:8080;
    server  10.0.0.12:8080;
    keepalive  64;   # 每个 worker 与后端保持的空闲长连接数
}

location / {
    proxy_pass  http://backend;
    proxy_http_version  1.1;
    proxy_set_header  Connection  "";   # 关键:清空 Connection 才能复用
}

没有 keepalive,Nginx 每次转发都要与后端重新三次握手、慢启动,高并发下这笔开销巨大。启用后连接被复用,延迟和 CPU 都明显下降——这是生产反代的必备优化。

6.7 一个完整均衡配置

upstream api_cluster {
    least_conn;
    server  10.0.0.11:8080  weight=2  max_fails=3  fail_timeout=15s;
    server  10.0.0.12:8080  weight=2  max_fails=3  fail_timeout=15s;
    server  10.0.0.13:8080  weight=1;
    server  10.0.0.99:8080  backup;
    keepalive  64;
}

server {
    listen  80;
    server_name  api.example.com;

    location / {
        proxy_pass  http://api_cluster;
        proxy_http_version  1.1;
        proxy_set_header  Connection  "";
        proxy_set_header  Host  $host;
        proxy_set_header  X-Forwarded-For  $proxy_add_x_forwarded_for;
        proxy_next_upstream  error timeout http_502 http_503;
    }
}

6.8 小结与下一步