Nginx 限流 ngx_http_limit_req_module
Nginx 限流模块 ngx_http_limit_req_module 完全指南
在高并发场景下,保护后端服务不被突发流量冲垮是架构设计中的重要一环。Nginx 通过 ngx_http_limit_req_module 模块提供了基于**漏桶算法(Leaky Bucket)**的请求频率限制功能,能够对单个 IP 或自定义键的请求速率进行精确控制。本教程将从零开始,带你掌握该模块的配置与最佳实践。
模块简介与工作原理
ngx_http_limit_req_module 默认编译进 Nginx,用于限制请求处理速率,而非连接数。其核心是漏桶算法:
- 请求以任意速率到达,首先进入一个“桶”(共享内存区域)。
- 桶以固定速率“漏水”(处理请求),多余请求会被暂存或直接拒绝。
- 如果桶已满,新请求将触发限制。
该模块通过两个核心指令协同工作:limit_req_zone 定义共享内存和速率参数,limit_req 在具体上下文中启用限制。
注意:该模块仅作用于 HTTP 请求处理阶段,对 TCP/UDP 层的流量无效(需使用
stream模块)。
核心配置指令详解
1. limit_req_zone —— 定义限流共享内存区域
语法:
limit_req_zone key zone=name:size rate=rate;
上下文:http
key:限流依据,通常是客户端 IP($binary_remote_addr),也可以是其它变量组合。zone:共享内存名称与大小(如mylimit:10m),10MB 大约可存储 16 万个 IP 状态。rate:请求速率,格式为r/s或r/m(如10r/s表示每秒 10 个请求)。
示例:
http {
limit_req_zone $binary_remote_addr zone=perip:10m rate=5r/s;
}
2. limit_req —— 启用限流规则
语法:
limit_req zone=name [burst=number] [nodelay | delay=number];
上下文:http, server, location
zone:引用limit_req_zone定义的区域名称。burst:允许的突发请求数,超出平均速率但未超过rate+burst的请求会被排队延迟处理。nodelay:与burst配合,让burst内的请求立即处理,不延迟;超出burst的直接拒绝。delay(商业版):更精细的延迟控制。开源版本不适用,此处不展开。
典型用法:
location /api/ {
limit_req zone=perip burst=10 nodelay;
}
3. limit_req_log_level —— 设置日志级别
语法:
limit_req_log_level info | notice | warn | error;
默认:error
上下文:http, server, location
当请求被限流时,Nginx 会记录一条错误日志。可设置为 warn 或 info 以减少日志干扰。
4. limit_req_status —— 自定义拒绝状态码
语法:
limit_req_status code;
默认:503
上下文:http, server, location
当请求被拒绝时返回的 HTTP 状态码。可改为 429 Too Many Requests 以符合 RESTful 惯例:
limit_req_status 429;
配置实战:从基础到进阶
场景一:简单的固定速率限流
全局限制每个 IP 每秒最多 2 个请求:
http {
limit_req_zone $binary_remote_addr zone=one:10m rate=2r/s;
server {
location / {
limit_req zone=one;
proxy_pass http://backend;
}
}
}
效果:超过 2r/s 的请求立返 503。
场景二:允许突发流量(burst + nodelay)
正常速率 1r/s,但允许瞬时 5 个突发请求,且不延迟处理:
http {
limit_req_zone $binary_remote_addr zone=two:10m rate=1r/s;
server {
location / {
limit_req zone=two burst=5 nodelay;
}
}
}
- 若客户端瞬时发送 6 个请求,前 6 个中,1 个按固定速率处理,剩下 5 个消耗突发额度立刻处理(因为
nodelay),超出 5 个的才拒绝。 - 若去掉
nodelay,则第 2~6 个请求会被排队,按固定速率依次处理,用户感知为延迟。
场景三:多条件精细限流
对特定 URI 设置不同速率:
http {
limit_req_zone $binary_remote_addr zone=static:10m rate=30r/s;
limit_req_zone $binary_remote_addr zone=login:10m rate=1r/m;
server {
location /static/ {
limit_req zone=static burst=20 nodelay;
}
location /login/ {
limit_req zone=login burst=2 nodelay;
limit_req_status 429;
}
}
}
静态资源允许较高频率,登录接口严格限制,有效防止暴力破解。
场景四:基于其他键的限流
除了 IP,还可以对 $http_apikey 或者 $server_name 限流。例如按 API 密钥限流:
limit_req_zone $http_apikey zone=apikey:10m rate=100r/s;
server {
location /api/ {
# 从查询参数或头部获取 api_key,这里假设头部
set $api_key $http_x_api_key;
limit_req zone=apikey;
}
}
注意:自定义变量需要在区域声明中直接使用,若需要在 limit_req_zone 中使用组合变量,需确保变量在请求处理早期可达。
测试与验证
使用压力测试工具 ab(ApacheBench)或 wrk 验证配置效果。
示例:假设单 IP 限速 2r/s,用 ab 发送 10 个并发请求:
ab -n 10 -c 10 http://example.com/api/test
查看返回状态码,非 2xx/3xx 的数量即为被拒绝请求。同时观察 Nginx 错误日志(默认 /var/log/nginx/error.log)中是否有 limiting requests 的记录。
注意事项:测试时请使用真实客户端 IP,确保没有被代理或前端设备替换。若 Nginx 位于反向代理之后,应使用 $http_x_forwarded_for 或 $realip_remote_addr(需配合 realip 模块)来获取真实 IP。
常见问题与最佳实践
-
共享内存不足?
zone=name:size,若内存耗尽,所有请求将被限流。预估容量:每个键占用约 64 字节,10MB 约可存 160,000 个键。监控内存使用,适当扩大。 -
burst 与 nodelay 如何搭配?
对实时性要求高的 API,建议使用nodelay,让用户在突发额度内无感知;对可容忍延迟的下载类服务,可仅用burst平滑处理。 -
如何返回更友好的错误信息?
limit_req_status 429; error_page 429 = @toomany; location @toomany { default_type application/json; return 429 '{"error":"too many requests"}'; } -
不要将 $remote_addr 直接作为 key
推荐使用$binary_remote_addr,它占用固定 4 字节(IPv4)或 16 字节(IPv6),比$remote_addr(字符串)节省内存并加快哈希速度。 -
与 limit_conn 的区别
limit_req控制请求速率,limit_conn控制并发连接数。两者可组合使用,形成双重防护。 -
白名单机制
对内部 IP 或特定 UA 放行,可通过if指令或geo映射变量实现,然后将该变量作为 key 条件限制值为空时跳过限制。
示例:geo $limit { default 1; 10.0.0.0/8 0; } map $limit $limit_key { 0 ""; 1 $binary_remote_addr; } limit_req_zone $limit_key zone=white:10m rate=10r/s;当
$limit_key为空时,limit_req不生效。
总结
ngx_http_limit_req_module 是 Nginx 防御突发流量、保护后端服务的利器。通过合理设置速率、突发缓冲和自定义键,你能实现从简单 IP 限流到复杂业务逻辑的流量整形。记住:先估算正常流量,再设置略高于峰值的限制,配合 burst 吸收突发,用 nodelay 提升用户体验,最后通过日志和监控持续迭代参数。
现在,打开你的 nginx.conf,尝试为关键接口加上一道限流保护吧。