3. Nginx限速模块:从配置到实战
限速这事儿,说白了就是给流量装上水龙头。DDoS打过来的时候,你不可能让所有请求都通过——服务器扛不住。Nginx的限速模块,就是帮你拧紧这个水龙头的工具。
我个人习惯把限速分成两类:请求频率限制和并发连接限制。前者管的是“每秒能来多少次”,后者管的是“同时能有多少人在线”。两个模块配合使用,效果最好。
3.1 limit_req_module:请求频率限制
这个模块控制的是单位时间内的请求次数。比如,我限制每个IP每秒只能发10个请求,超过的就直接返回503。
先看基本配置:
http {
# 定义限速区域
limit_req_zone $binary_remote_addr zone=mylimit:10m rate=10r/s;
server {
location /api/ {
limit_req zone=mylimit;
}
}
}
这里有几个关键点:
- $binary_remote_addr:用客户端IP作为限速的key。用二进制格式比$remote_addr省内存。
- zone=mylimit:10m:定义一个叫mylimit的共享内存区域,大小10MB。10MB大概能存16万个IP状态。
- rate=10r/s:速率限制,每秒10个请求。
但有个问题——上面的配置太死板。如果客户端在1秒内发了11个请求,第11个直接就被拒绝了。这在真实业务中不太合理。用户可能只是手抖了一下,多点了一次刷新。
这时候就需要burst参数了。
3.2 burst与nodelay:让限速更智能
burst,说白了就是个缓冲区。允许请求在短时间内超过限制,但超出的部分先排队,慢慢处理。
location /api/ {
limit_req zone=mylimit burst=20;
}
这个配置的意思是:速率还是10r/s,但允许瞬间最多有20个请求排队。超过20个的,直接拒绝。
举个例子:
- 第1秒来了30个请求 → 10个立即处理,20个进入burst队列,每秒处理10个
- 第2秒又来了30个请求 → 此时burst队列里还有10个没处理完,新来的只能再排20个,剩下的10个被拒绝
但这样又有个问题——排队的请求会延迟响应。用户可能等得不耐烦,直接超时重试了。重试又会产生新请求,恶性循环。
所以有了nodelay参数:
location /api/ {
limit_req zone=mylimit burst=20 nodelay;
}
nodelay的意思是:burst队列里的请求不延迟处理,只要队列没满,来了就立即处理。但速率限制依然生效——处理完这批突发请求后,后续请求必须按10r/s的速率来。
- 不加nodelay:请求排队,延迟响应,但速率平滑
- 加nodelay:请求立即响应,但速率可能瞬间飙高(不超过burst值)
我在项目中遇到过一个问题:加了nodelay之后,后端数据库压力突然增大。后来发现是因为burst设得太大,瞬间放进去太多请求。嗯,这里要注意——burst值要根据后端处理能力来设,不是越大越好。
3.3 limit_conn_module:并发连接限制
请求频率限制管的是“每秒来多少”,并发连接限制管的是“同时连多少”。这两个概念容易混淆,我简单解释一下:
- 请求频率:1秒内发100个请求,但可能只用了1个连接(HTTP/2或Keep-Alive)
- 并发连接:同时打开100个TCP连接,但每个连接可能只发1个请求
DDoS攻击经常是大量并发连接,每个连接发少量请求。这时候limit_req可能防不住,因为请求频率不高。但limit_conn能直接掐断连接数。
配置示例:
http {
limit_conn_zone $binary_remote_addr zone=addr:10m;
server {
location /download/ {
limit_conn addr 10;
}
}
}
这个配置限制每个IP最多同时建立10个连接。超过的,Nginx直接返回503。
还可以限制总连接数:
http {
limit_conn_zone $server_name zone=server:10m;
server {
limit_conn server 1000; # 整个server最多1000个并发连接
}
}
3.4 两个模块的协同配置
实际生产中,我通常把两个模块一起用。一个管频率,一个管并发,互相补充。
一个典型的配置模板:
http {
# 请求频率限制
limit_req_zone $binary_remote_addr zone=req_limit:10m rate=30r/s;
# 并发连接限制
limit_conn_zone $binary_remote_addr zone=conn_limit:10m;
server {
listen 80;
server_name api.example.com;
location / {
# 先检查并发连接数
limit_conn conn_limit 20;
# 再检查请求频率
limit_req zone=req_limit burst=50 nodelay;
# 错误响应
limit_req_status 429;
limit_conn_status 503;
proxy_pass http://backend;
}
}
}
这个配置的效果:
- 每个IP最多同时20个连接
- 每秒最多30个请求,允许50个突发(立即处理)
- 超限的返回429(Too Many Requests)或503(Service Unavailable)
3.5 核心逻辑流程图
下面这张图展示了请求进入Nginx后的限速处理流程:
3.6 参数对比与选择建议
| 参数 | 作用 | 适用场景 | 注意事项 |
|---|---|---|---|
| rate | 基准速率,单位r/s或r/m | 所有场景必配 | 根据后端处理能力设定,一般不超过后端TPS的80% |
| burst | 突发缓冲区大小 | 允许短时间流量波动的场景 | 不宜过大,否则失去限速意义。建议不超过rate的5倍 |
| nodelay | 突发请求不延迟处理 | 对响应时间敏感的业务 | 配合burst使用,单独使用无效。注意后端压力 |
| limit_conn | 限制并发连接数 | 防止连接耗尽、慢速攻击 | 与limit_req互补,建议同时配置 |
最后说一句:限速配置不是一劳永逸的。上线后要盯着监控看,根据实际流量调整参数。我一般先用宽松的配置跑一周,收集基线数据,再逐步收紧。这样既不影响正常用户,又能有效防御攻击。