在高并发、分布式系统中,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 秒) |
故障恢复流程
- 后端因错误被标记为
down; - 在
fail_timeout期间,Nginx 不会转发请求给它; - 超时后,Nginx 自动尝试发送一个请求进行试探;
- 若成功,则恢复;若失败,重新计数。
✅ 这就是“被动恢复”——靠真实流量探测后端是否恢复。
三、哪些情况会触发重试?
默认只将 连接错误(如 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,语义清晰。
🌟 优秀的系统不是没有故障,而是能优雅地处理故障。