在高并发、分布式系统中,Nginx 不仅是反向代理,更是服务稳定性的第一道防线。最近在客户线程配合客户做一些高可用测试,发现一些功能完全可以使用Nginx的配置来处理。

📌 本文适用于 Nginx 开源版(非 Plus),所有配置均经过生产环境验证。


一、负载均衡策略选择

Nginx 的 upstream 模块支持多种负载均衡算法,合理选择能显著提升系统稳定性与资源利用率。

upstream backend {
    least_conn;  # 推荐策略
    server 10.0.0.1:8000 max_fails=3 fail_timeout=30s;
    server 10.0.0.2:8000 max_fails=3 fail_timeout=30s;
}

常见策略对比

策略 行为 适用场景
round_robin(默认) 轮流分配请求 请求处理时间均匀(如简单 CRUD 接口)
least_conn 分配给当前连接数最少的后端 请求耗时差异大、存在长连接或批处理任务(推荐)
ip_hash 同一客户端 IP 固定路由到同一后端 需要会话保持(不推荐,应优先考虑应用层 Session 共享)
hash $request_uri 相同 URI 路由到同一后端 利用缓存局部性(如 CDN 或本地缓存)

💡 建议:除非有强会话绑定需求,否则优先使用 least_conn

二、健康检查与故障恢复机制

Nginx 开源版仅支持 被动健康检查

关键参数说明

参数 说明
max_fails=3 fail_timeout 时间窗口内,连续失败 ≥3 次,则标记该 server 为不可用(down)
fail_timeout=30s 1. 失败计数的时间窗口(30 秒) 2. 被标记为 down 后,暂停使用该 server 的时长(30 秒)

故障恢复流程

  1. 后端因错误被标记为 down
  2. fail_timeout 期间,Nginx 不会转发请求给它;
  3. 超时后,Nginx 自动尝试发送一个请求进行试探;
  4. 若成功,则恢复;若失败,重新计数。

✅ 这就是“被动恢复”——靠真实流量探测后端是否恢复。

三、哪些情况会触发重试?

默认只将 连接错误(如 connection refused)或 超时 视为失败。 HTTP 5xx 响应(如 500/502/503)默认不算失败!

若希望对 5xx 也重试,需显式配置:

location / {
    proxy_pass http://backend;
    proxy_next_upstream error timeout http_500 http_502 http_503 http_504;
    proxy_next_upstream_tries 3;
    proxy_next_upstream_timeout 10s;
}

🔔 注意:仅对幂等请求(GET/HEAD/DELETE)启用 5xx 重试。

四、超时配置详解

超时是 502/504 错误的主要原因。三个关键参数:

  • proxy_connect_timeout:建立 TCP 连接的超时
  • proxy_send_timeout:发送请求体的间隔超时
  • proxy_read_timeout:读取响应体的间隔超时

超时参数建议

🎯 黄金法则:超时 = 后端 P99 × 1.5 ~ 2.0

五、限流配置:防刷 & 防 DoS

1. 并发连接数限制(limit_conn

http {
    limit_conn_zone $binary_remote_addr zone=conn_limit:10m;
    limit_conn_status 429;

    server {
        limit_conn conn_limit 40;

        location / {
            proxy_pass http://backend;
        }
    }
}
  • limit_conn_zone:定义一个共享内存区域,用于跟踪每个 key 的当前并发连接数。
  • $binary_remote_addr:使用客户端 IP 的二进制形式作为 key(节省内存,等效于 $remote_addr 但更高效)。
  • zone=limitbyaddr:10m
    • 创建一个名为 limitbyaddr 的共享内存区;
    • 大小为 10MB,可支持约 16 万个并发 IP 的连接状态记录(每个记录约 32–64 字节)。
  • 在该 server(或也可以在 location)中启用连接限制。
  • 当客户端的并发连接数超过限制时,Nginx 返回 HTTP 429 (Too Many Requests) 状态码。
  • 默认情况下,Nginx 会返回 503,这里显式指定为 429,语义更清晰(表示“请求过多”或“连接过多”)。
  • limitbyaddr:引用前面定义的 limit_conn_zone 名称。
  • 40:表示每个客户端 IP 最多允许同时建立 40 个连接到该 server(或 location)。
  • limit_conn_status 429;返回 429 Too Many Requests

2. 请求速率限制(limit_req

http {
    limit_req_zone $binary_remote_addr zone=req_rate:10m rate=3r/s;
    limit_req_status 429;

    server {
        limit_req zone=req_rate burst=5 nodelay;

        location /api/ {
            proxy_pass http://backend;
        }
    }
}
    • limit_req_zone:定义一个共享内存区域,用于存储限流状态(比如每个 IP 的请求计数)。
  • $binary_remote_addr:使用客户端的二进制格式 IP 地址作为限流的 key(比 $remote_addr 更节省内存)。
  • zone=limitbyaddr:10m
    • 创建一个名为 limitbyaddr 的共享内存区;
    • 大小为 10MB,大约可存储 16 万个 IP 地址的状态(每个 IP 状态约占用 64 字节)。
  • rate=3r/s:限制每个 IP 的请求速率为 每秒最多 3 个请求(即平均 1 秒允许 3 个请求)。
  • burst=5 允许突发 5 次;
  • nodelay 表示突发请求立即处理。

对比:limit_req vs limit_conn

特性 limit_req limit_conn
限制对象 请求速率 并发连接数
适用场景 防爬虫、防刷接口 防 DoS、防连接耗尽
工作层级 HTTP 层 连接层
受 Keep-Alive 影响

六、总结与最佳实践

  • 使用 least_conn 作为默认负载策略;
  • 通过 max_fails + fail_timeout 实现自动故障隔离与恢复;
  • 显式配置 proxy_next_upstream 以重试 5xx 错误(仅限幂等接口);
  • 超时设置务必基于后端实际性能;
  • 双重限流:limit_conn + limit_req,防御不同维度的攻击;
  • 统一返回 429,语义清晰。

🌟 优秀的系统不是没有故障,而是能优雅地处理故障。

参考资料