Nginx限流、排队与拒绝全过程
限流不是“流量大就返回503”。一个可用的限流方案必须回答:按谁限、平均速率是多少、允许多大突发、突发是立即放行还是延迟、超过阈值返回什么、多个Worker是否共享、多个Nginx实例是否共享,以及限流失败时保护谁、误伤谁。
学习目标
学完后应能:
- 区分速率限制、并发限制、连接限制和业务配额。
- 解释
limit_req_zone共享内存中的Key、时间和超额状态。 - 用时间线推导rate、burst、nodelay和delay。
- 说明burst是可容纳的超额请求数量,不是“每秒额外额度”。
- 解释nodelay立即放行后为什么仍然产生速率债务。
- 区分limit_req漏桶式整形和令牌桶允许突发的方式。
- 解释limit_conn何时计数,以及HTTP/2/3与WebSocket边界。
- 正确处理真实IP、NAT、CDN、租户和用户Key。
- 说明单机共享zone与多Nginx实例全局限流的区别。
- 为登录、短信、搜索、上传和Webhook设计分层商业策略。
一、限流究竟保护什么
flowchart TD
A["客户端流量"] --> B["Nginx入口限流"]
B --> C["应用线程池"]
C --> D["数据库连接池"]
D --> E["MySQL、Redis、MQ和外部接口"]入口限流的目标通常是保护最慢、最贵或最脆弱的共享资源:
- 登录密码校验CPU和风控服务。
- 短信供应商额度与成本。
- 搜索集群CPU和复杂查询。
- 文件上传带宽、磁盘和对象存储。
- Java线程池和数据库连接池。
- 支付、库存等核心链路。
如果不知道要保护的资源容量,rate=100r/s只是猜测。
二、四类限制先分清
| 类型 | 限制对象 | 典型指标 | Nginx能力 |
|---|---|---|---|
| 请求速率 | 一段时间进入多少请求 | r/s、r/m | limit_req |
| 并发请求 | 同时处理多少请求 | in-flight requests | limit_conn可近似部分场景 |
| TCP连接 | 同时多少网络连接 | established connections | 还需系统、防火墙、stream等层治理 |
| 业务配额 | 用户每天可发几条短信 | 次/日、金额/日 | 通常应由业务/网关状态服务实现 |
Nginx的IP速率限制不能代替用户配额。例如同一用户换IP后,按IP计数会重新开始;同一公司NAT下多人又可能共享一个IP被误伤。
三、limit_req由两类指令组成
在http层定义共享状态区:
limit_req_zone $binary_remote_addr zone=login_ip:20m rate=5r/s;在server或location中应用:
location = /api/login {
limit_req zone=login_ip burst=10 nodelay;
proxy_pass http://auth_backend;
}分工:
limit_req_zone:定义Key、共享内存区名称和大小、平均速率。limit_req:把某个zone应用到请求路径,并配置burst、delay/nodelay。
只定义zone但不应用,路径不会被限制;只写limit_req但没有同名zone,配置检查会失败。
四、共享内存中保存什么
对每个非空Key,Nginx需要保存类似:
- Key值或其紧凑表示。
- 最近请求时间。
- 当前超额/欠债状态。
- 红黑树等索引节点元数据。
- 内存管理开销。
flowchart TD
A["请求到达"] --> B["计算限流Key"]
B --> C{"Key是否为空"}
C -- "是" --> D["通常不参与该zone计数"]
C -- "否" --> E["在共享内存查找Key状态"]
E --> F["按经过时间衰减旧的超额量"]
F --> G["加入当前请求的消耗"]
G --> H["判断立即、延迟或拒绝"]
H --> I["更新Key状态"]共享内存让同一Nginx实例的多个Worker看到同一zone状态,避免每个Worker各给一份额度。
五、rate的准确含义
rate=10r/s表示长期平均速率每秒10个请求,可换算为平均100ms消化一个请求的超额量:
1秒 / 10 = 100毫秒它不表示:
- 每个自然秒零点重置10张票。
- 任意1秒窗口绝对只能看到10个请求。
- 后端一定每100ms完成一个请求。
- QPS容量就是10。
Nginx根据请求到达时间连续计算状态,不依赖自然秒边界。
六、不配置burst时发生什么
limit_req_zone $binary_remote_addr zone=one:10m rate=1r/s;
location /api/ {
limit_req zone=one;
}假设同一个Key在短时间发送多个请求:
t=0ms 请求A到达:允许
t=100ms 请求B到达:距离下一个平均额度还很早,拒绝
t=500ms 请求C到达:仍可能超额,拒绝
t=1000ms 请求D到达:速率债务大致消化,可允许精确边界受实现的时间精度和状态计算影响,但核心是:没有burst时几乎不容纳突发超额请求。
七、burst到底是什么
limit_req zone=one burst=5;burst=5表示每个Key最多容纳一定数量的超额请求状态。默认情况下,这些超额请求会被延迟,使它们按rate逐步获得处理时机;超过burst容量的请求被拒绝。
它不是:
- 每秒额外5个永久额度。
- 后端并发上限5。
- 队列最多占5个线程。
- 请求一定在5秒内完成。
延迟中的请求仍占用客户端连接、Nginx请求对象、内存和定时器。大量排队会增加入口连接和用户延迟,不能把Nginx当无限消息队列。
八、burst默认延迟的时间线
配置:
limit_req_zone $binary_remote_addr zone=one:10m rate=2r/s;
location /api/ {
limit_req zone=one burst=3;
}平均每500ms消化一个请求。若同一Key瞬间到达4个请求,可用简化模型理解:
请求A:立即处理
请求B:延迟到约500ms
请求C:延迟到约1000ms
请求D:延迟到约1500ms
再来请求E:超过burst,拒绝实际边界取决于请求到达微小时间差和实现计算。这个时间线用于理解“整形”,不是承诺精确调度时刻。
flowchart TD
A["瞬时请求"] --> B["第一个直接通过"]
A --> C["burst内超额请求进入延迟状态"]
C --> D["按rate逐步唤醒"]
A --> E["超过burst"]
E --> F["立即拒绝"]九、nodelay不是免费突发额度
limit_req zone=one burst=5 nodelay;nodelay让burst范围内的超额请求立即放行,不等待rate节奏,但这些请求仍被计入超额状态,形成“速率债务”。之后债务按rate随时间消化。
简化时间线,rate=1r/s burst=5 nodelay:
t=0瞬间:请求A和最多5个超额请求可立即通过
紧接着的新请求:burst状态已占满,会被拒绝
之后每经过约1秒:债务逐步减少,重新腾出一部分突发空间所以nodelay可能把瞬时压力直接交给后端。只有后端确实能承受该突发并发时才使用。
十、delay参数如何分两段处理突发
limit_req zone=one burst=12 delay=8;可以理解为:
- 一部分超额请求在delay阈值范围内立即通过。
- 超过delay但仍在burst范围内的请求按rate延迟。
- 超过burst的请求拒绝。
flowchart TD
A["请求到达并计算超额量"] --> B{"在正常速率内"}
B -- "是" --> C["立即处理"]
B -- "否" --> D{"超额量是否不超过delay"}
D -- "是" --> E["立即处理但记录债务"]
D -- "否" --> F{"是否仍在burst内"}
F -- "是" --> G["延迟到rate允许的时刻"]
F -- "否" --> H["拒绝"]不同Nginx版本对 delay= 的支持时间不同,必须在目标版本执行 nginx -V和 nginx -t。
十一、nodelay与delay的关系
概念上:
- 默认:burst内超额请求都延迟。
delay=N:前N份超额可立即通过,之后到burst之间延迟。nodelay:burst范围内超额都立即通过,可理解为不对burst内请求做延迟。
它们改变的是突发请求的放行时间,不会把长期平均rate改大。
十二、为什么不能只看平均QPS
假设后端长期可处理100r/s,但瞬时只能承受200个并发。若配置:
rate=100r/s burst=500 nodelay500个超额请求可能瞬间进入后端,压垮线程池和数据库连接池。平均rate正确,突发设计仍然错误。
必须同时评估:
- 长期安全吞吐。
- 瞬时安全并发。
- 平均响应时间。
- P99。
- Java线程池和队列。
- 数据库连接池。
- 单请求内存与外部调用。
十三、漏桶和令牌桶不是完全等价
漏桶式整形
请求进入桶或形成超额状态
→ 按稳定速率流出
→ 桶满后拒绝优势是输出更平滑,适合保护不能承受突发的后端。
令牌桶
按固定速率生成令牌
→ 空闲时令牌可积累到桶容量
→ 请求消耗令牌
→ 有积累令牌时允许突发
→ 令牌不足时等待或拒绝令牌桶天然允许使用历史空闲积累突发;经典漏桶输出更趋向恒定。二者都能做限速,但状态含义和突发表现不同,不能说“底层逻辑完全一样”。
Nginx limit_req通常用漏桶式/超额量模型解释更准确,burst+nodelay让它具备立即接受一定突发的行为,但仍不等于一个会长期积累空闲令牌的标准令牌桶。
十四、limit_req的状态码和日志
生产API通常希望超限返回429,而不是默认503:
limit_req_status 429;
limit_req_log_level warn;429表示Too Many Requests,更符合客户端语义。是否添加 Retry-After、返回JSON错误体、客户端如何退避,要结合API规范实现。
可配置dry run观察而不真正拒绝:
limit_req_dry_run on;版本支持需验证。Dry run阶段要记录命中量、Key分布、误伤用户和预期状态,不能开启后无人分析。
十五、limit_req状态变量
现代版本可通过类似 $limit_req_status 的变量记录请求状态,例如通过、延迟、拒绝或dry-run结果,具体值以版本文档为准。
日志示例:
log_format limit_main
'$request_id $remote_addr "$request" status=$status '
'limit_req=$limit_req_status request_time=$request_time';只有总429数量不够,还要按:
- URI。
- Key维度。
- 客户端类型。
- 租户。
- 版本。
- 上游响应时间。
分析是否遭受攻击、真实高峰或配置误伤。
十六、zone容量怎么规划
limit_req_zone $binary_remote_addr zone=api_ip:20m rate=20r/s;zone需要为活跃Key状态分配内存。容量取决于:
- Key长度。
- IPv4/IPv6。
- Nginx位数和版本。
- 状态结构和内存对齐。
- 同时活跃Key数量。
- 状态淘汰和过期行为。
$binary_remote_addr通常比文本 $remote_addr 更节省空间。不要把“10MB固定支持多少IP”当永久常量,应按目标版本文档、压测和error log验证。
zone耗尽时,Nginx可能尝试清理旧状态;无法分配新状态时,请求可能被拒绝并记录错误。zone过大也会占共享内存,需按业务基数规划。
十七、Key为空为什么危险
某些zone配置中,Key为空的请求通常不会被计数。例如按自定义Header:
limit_req_zone $http_x_tenant_id zone=tenant:20m rate=100r/s;如果客户端不带Header,Key为空,可能绕过该zone;如果Header可伪造,攻击者不断换值也能分散额度。
业务Key必须来自可信认证结果,而不是未经校验的客户端输入。开源Nginx原生是否能得到可信JWT Claim取决于模块和架构,常见做法是由可信API Gateway认证后传递受保护Header,或在业务网关/Redis中做用户级全局限流。
十八、真实IP信任链
错误做法:直接取任意请求的X-Forwarded-For第一个值。
正确示意:
# 必须替换为真实LB/CDN出口网段
set_real_ip_from 10.0.0.0/8;
real_ip_header X-Forwarded-For;
real_ip_recursive on;
limit_req_zone $binary_remote_addr zone=login_ip:20m rate=5r/s;flowchart TD
A["客户端可伪造XFF"] --> B["请求是否来自可信代理网段"]
B -- "否" --> C["忽略伪造链,使用连接来源"]
B -- "是" --> D["按realip规则还原代理链"]
D --> E["得到可信remote_addr口径"]
E --> F["用于限流与日志"]如果CDN出口网段变化而未更新,真实用户可能全部被识别成代理IP,造成大面积误伤。
十九、按IP限流为什么会误伤NAT用户
同一公司、学校或运营商出口可能有成千上万用户共享一个公网IP。按IP限制登录5r/s可能让正常用户互相争抢同一额度。
分层策略:
- IP维度:阻止单来源洪峰,阈值相对宽。
- 账号维度:防止针对单账号爆破,必须在可信认证/业务层实现。
- 设备维度:辅助风控,不能单独信任客户端设备ID。
- 全局维度:保护后端总容量。
- 风险维度:验证码、黑名单和行为分析。
二十、多维Key与组合Key
现代Nginx版本支持使用多个变量组成Key,例如概念上:
limit_req_zone "$binary_remote_addr:$server_name" zone=ip_host:20m rate=20r/s;组合Key可避免多个虚拟主机共享同一IP额度,但Key更长、状态更多、zone内存更大。要确认目标版本对多变量Key的支持,并避免可控参数造成Key基数攻击。
二十一、limit_conn限制的不是所有TCP连接
定义状态区:
limit_conn_zone $binary_remote_addr zone=conn_ip:20m;
limit_conn_zone $server_name zone=conn_server:10m;应用:
server {
limit_conn conn_server 1000;
location /download/ {
limit_conn conn_ip 3;
}
}HTTP模块通常在请求Header被完整读取、请求进入处理后才对该请求计数,因此它不等于防火墙看到的所有TCP established连接。慢速发送Header的攻击还需要连接层超时、防火墙、WAF和系统参数治理。
二十二、HTTP/2、HTTP/3和limit_conn
HTTP/1.1中连接与并发请求关系相对直观;HTTP/2和HTTP/3可以在一个网络连接上并发多个stream。Nginx文档通常把每个并发HTTP/2/3请求作为limit_conn意义上的独立连接计数,而不是只算底层TCP/QUIC连接一次。
因此:
limit_conn 10不能简单解释成“客户端最多建立10条TCP连接”。必须按协议版本和请求并发验证。
二十三、WebSocket和长下载
WebSocket升级后会长期占用请求/连接状态。长下载也会持续占用连接、带宽和文件FD。此时limit_conn比单纯rate更有价值:
- rate限制建立新长连接的速度。
- conn限制同一Key同时保持多少长连接。
- 带宽还需
limit_rate、CDN或专门传输系统治理。
不能把大文件和普通API使用同一阈值。
二十四、limit_conn状态和dry run
可配置:
limit_conn_status 429;
limit_conn_log_level warn;
limit_conn_dry_run on;版本支持要验证。日志可结合 $limit_conn_status 等变量观察命中情况,具体变量值以目标版本为准。
二十五、同一Nginx的多个Worker是否共享
limit_req_zone和 limit_conn_zone使用共享内存,同一Nginx master下的Worker通常共享状态:
Worker 1收到用户A请求
→ 更新共享zone
Worker 2随后收到用户A请求
→ 读取到同一Key的新状态这与把计数放在Worker普通进程内存不同。reload期间新旧Worker如何访问共享zone由Nginx管理,但修改zone名称、Key或大小属于配置状态变化,需要金丝雀和验证。
二十六、多Nginx实例为什么不共享额度
flowchart TD
A["上层LB"] --> B["Nginx A:自己的zone"]
A --> C["Nginx B:自己的zone"]
A --> D["Nginx C:自己的zone"]三台Nginx配置每IP 10r/s,并不天然形成全局10r/s。若请求均匀分散,理论总放行可能接近30r/s,且每台还有自己的burst。
这意味着扩容Nginx会改变总限流能力。生产必须明确阈值是:
- 每实例。
- 每节点。
- 每可用区。
- 还是全局。
二十七、如何实现全局限流
可选方式:
| 方案 | 优点 | 风险与边界 |
|---|---|---|
| 上层统一API Gateway | 单点策略和认证上下文清晰 | 网关自身需高可用 |
| Redis/Lua计数 | 多实例共享状态 | 网络延迟、Redis故障、热Key |
| 专用限流服务 | 策略复杂、可统一审计 | 增加关键依赖和调用成本 |
| 一致性路由到固定限流节点 | 降低分布式共享需求 | 节点故障和重映射 |
| 本地近似限流加全局配额 | 性能高、可分层保护 | 精确性与额度分配复杂 |
全局强精确限流通常会引入同步共享状态,性能、可用性和一致性需要权衡。保护系统时,近似但可用的本地限流有时比依赖已故障的远程计数器更可靠。
二十八、限流服务故障时放行还是拒绝
这是业务决策:
- Fail-open:限流依赖故障时放行,保证可用,但后端可能被压垮。
- Fail-closed:依赖故障时拒绝,保护资源,但正常用户不可用。
- 分层降级:本地保底阈值继续保护,复杂全局配额降级。
支付、短信、登录、搜索的选择不同。必须在设计阶段写清,而不是故障时临时决定。
二十九、限流和排队的容量关系
排队等待时间近似取决于:
前方超额请求数 / rate例如rate=10r/s,前方有20个延迟请求,尾部请求可能等待约2秒量级。若客户端超时只有1秒,这些请求还没轮到就已断开,排队只会浪费连接和内存。
设计burst时要结合:
- 客户端超时。
- Nginx超时。
- 后端SLA。
- 可接受排队时延。
- 连接和内存容量。
三十、登录接口商业策略
目标同时是防爆破和保护密码校验资源。
示意:
limit_req_zone $binary_remote_addr zone=login_ip:20m rate=5r/s;
location = /api/login {
limit_req zone=login_ip burst=10 nodelay;
limit_req_status 429;
proxy_pass http://auth_backend;
}还需要业务层:
- 账号维度失败次数。
- IP+账号组合。
- 验证码和风险评分。
- 密码哈希CPU容量。
- 避免用户名枚举。
- 审计和告警。
仅按IP会误伤NAT用户,仅按账号会被攻击者轮换账号。
三十一、短信接口商业策略
短信涉及真实成本,应至少有:
- IP分钟级速率。
- 用户分钟/小时/天配额。
- 手机号配额。
- 模板配额。
- 全局供应商额度。
- 验证码有效期内防重复发送。
- 幂等请求号。
- 风控黑名单。
Nginx只能做入口粗限流,手机号和用户配额必须在可信业务层实现,不能从未验证JSON Body直接构造Nginx Key。
三十二、搜索接口商业策略
普通搜索和复杂聚合成本不同:
- 简单关键词查询阈值较高。
- 深分页、聚合、导出阈值更低。
- 匿名用户按IP宽限流。
- 登录用户按租户/用户配额。
- 全局限制保护ES线程池。
- 慢查询、超时和查询复杂度单独治理。
只按请求次数会让一个极重查询和一个轻查询获得同样额度,需要业务层成本权重。
三十三、上传与下载策略
上传需要限制:
- 新请求速率。
- 同时上传连接。
- 请求体大小。
- 上传超时。
- 每用户存储配额。
- 病毒扫描和文件类型。
下载需要:
- 并发连接。
- 单连接带宽。
- CDN分发。
- 防盗链和签名URL。
limit_req不等于带宽限速,limit_conn也不等于文件配额。
三十四、Webhook与回调策略
支付回调、第三方Webhook可能合法重试。限流过严会拒绝关键通知;完全放开又可能被伪造洪峰攻击。
需要:
- 来源签名验证。
- 时间戳和Nonce防重放。
- IP白名单只能辅助。
- 快速落库/入队后响应。
- 幂等事件ID。
- 合理burst吸收第三方重试波峰。
- 失败补偿和对账。
三十五、完整分层配置Demo
http {
# 示例可信代理网段,生产替换
set_real_ip_from 10.0.0.0/8;
real_ip_header X-Forwarded-For;
real_ip_recursive on;
limit_req_zone $binary_remote_addr zone=api_ip:20m rate=20r/s;
limit_req_zone $binary_remote_addr zone=login_ip:20m rate=5r/s;
limit_conn_zone $binary_remote_addr zone=conn_ip:20m;
limit_conn_zone $server_name zone=conn_server:10m;
log_format limited '$request_id $remote_addr "$request" '
'status=$status req_limit=$limit_req_status '
'conn_limit=$limit_conn_status '
'request_time=$request_time';
server {
listen 8080;
access_log /dev/stdout limited;
limit_conn conn_server 1000;
limit_req_status 429;
limit_conn_status 429;
location = /api/login {
limit_req zone=login_ip burst=10 nodelay;
limit_conn conn_ip 3;
proxy_pass http://auth_backend;
}
location /api/search/ {
limit_req zone=api_ip burst=40 delay=10;
limit_conn conn_ip 10;
proxy_pass http://search_backend;
}
location /download/ {
limit_req zone=api_ip burst=10;
limit_conn conn_ip 3;
proxy_pass http://file_backend;
}
}
}该Demo只演示Nginx本地策略。生产必须根据可信代理网段、真实容量、多个入口实例和业务配额调整。
三十六、可运行时间线实验
先启动一个监听8081的健康回显后端,再配置:
limit_req_zone $binary_remote_addr zone=lab:10m rate=2r/s;
server {
listen 8090;
location /default-delay {
limit_req zone=lab burst=3;
proxy_pass http://127.0.0.1:8081;
}
}这里必须使用真正进入content阶段的静态文件或proxy处理器。不要为了省事写 return 200 做限流实验:return属于rewrite模块并可能在preaccess阶段的limit_req处理器执行前就结束请求,导致测试错误地显示“限流不生效”。
记录每次请求开始、HTTP状态和总时间:
for i in 1 2 3 4 5 6; do
curl -s -o /dev/null \
-w "request=$i code=%{http_code} time=%{time_total}\n" \
http://127.0.0.1:8090/default-delay
done分别替换为:
limit_req zone=lab burst=3 nodelay;和:
limit_req zone=lab burst=3 delay=1;比较:
- 哪些请求立即完成。
- 哪些请求延迟。
- 哪些请求被拒绝。
- 突发结束后多久恢复额度。
不同测试请求必须使用同一Key;若经过代理,先确认 $remote_addr没有变化。
三十七、限流未生效排查
nginx -T确认zone与应用位置。- 确认请求命中预期server/location。
- 检查Key是否为空。
- 检查Key是否每次变化。
- 检查真实IP是否被代理错误覆盖。
- 检查是否访问不同Nginx实例。
- 检查是否处于dry run。
- 检查rate、burst和delay是否过宽。
- 查看
$limit_req_status与error log。 - 检查配置修改后reload是否成功。
三十八、限流误伤排查
- 按Key和URI统计429。
- 检查NAT/CDN是否让大量用户共享IP。
- 检查客户端是否并发重试放大。
- 检查移动端超时后自动重发。
- 检查burst排队时间是否超过客户端超时。
- 检查多个接口是否错误共用同一zone额度。
- 检查发布后实例数变化是否改变总额度。
- 检查业务真实峰值与后端容量。
三十九、zone内存异常排查
观察error log中的共享内存分配、状态淘汰和拒绝信息。检查:
- 活跃Key基数是否突然上涨。
- Key是否包含随机请求ID导致每请求新状态。
- 攻击者是否伪造高基数Header。
- IPv6比例和Key长度。
- zone大小。
- 状态是否按预期过期。
不要只无限增大zone;先修复不可控Key和攻击面。
四十、商业场景一:扩Nginx后限流翻倍
原来一台Nginx限制每IP 10r/s,扩成三台后,上层LB随机分发请求,每台都有独立zone,同一IP理论上可获得接近30r/s总额度。
修复方向:
- 明确阈值是每实例还是全局。
- 按实例数分配本地额度并留误差。
- 在统一API Gateway或Redis限流服务做全局额度。
- 扩缩容流程同步更新限流容量并压测。
四十一、商业场景二:登录限流被XFF绕过
攻击者直接发送不同X-Forwarded-For,旧配置把Header第一个IP作为Key,每次都创建新额度和新zone状态。
修复:只信任明确LB/CDN网段,由realip模块还原地址;非可信来源的XFF仅作为普通不可信输入。结合账号、设备和风险策略,不能只按IP。
四十二、商业场景三:排队请求全部客户端超时
配置rate很低、burst很大且默认delay。大量请求留在Nginx等待数秒,但移动端1秒超时后主动断开并重试,造成:
- 排队连接和内存增加。
- 客户端重试流量增加。
- 用户仍然失败。
- 后端随后收到过期请求。
修复:按SLA限制最大排队时延,必要时快速429让客户端指数退避;对必须可靠处理的任务应入MQ,而不是在Nginx连接上长时间排队。
四十三、商业场景四:短信接口只按IP被薅
攻击者使用代理IP池轮换来源,每个IP都获得新额度。Nginx IP限流看起来正常,但手机号和用户总发送量失控。
治理:业务层按用户、手机号、设备、模板和全局供应商额度控制,使用幂等请求号、验证码有效期防重复、风险评分和成本告警;IP限流只作为第一层。
四十四、面试标准回答
rate、burst和nodelay分别是什么
rate定义每个Key的长期平均请求速率;burst定义可容纳的超额请求数量,默认超额请求按rate延迟,超过burst拒绝;nodelay让burst范围内的超额请求立即放行,但仍记录速率债务,后续突发空间要随时间恢复,不是永久增加额度。
burst是不是每秒额外放行数量
不是。它是某个Key当前可容纳的超额状态上限,与请求到达时间和债务消化有关,不按自然秒重置。默认可形成延迟队列,nodelay时立即放行但占满突发空间;超过burst仍拒绝。
limit_req多个Worker是否共享
同一Nginx实例的Worker通过limit_req_zone共享内存共享Key状态,因此不会每Worker各给一份额度;但不同主机或Pod的zone不共享,三个实例各10r/s可能形成约30r/s总额度,全球限流要用统一网关或共享状态服务。
limit_conn限制的是TCP连接数吗
不完全是。HTTP模块通常在请求Header读完并进入处理后计数,不包含所有尚未形成请求的TCP连接;HTTP/2/3中并发stream可能分别计数。慢Header攻击、SYN洪峰和系统连接上限还要在超时、防火墙和内核层治理。
为什么不能直接按X-Forwarded-For限流
它是客户端可伪造的HTTP Header。只有请求来自明确可信LB/CDN网段时,才能由realip模块按代理链还原客户端地址;否则攻击者轮换伪造值即可绕过额度并制造高基数Key。
Nginx限流和Redis全局限流怎么选
Nginx本地zone性能高、故障依赖少,适合入口保底,但多实例不共享;Redis/Lua可做用户或租户全局额度,但增加网络延迟、热Key和Redis故障处理。商业系统常用Nginx本地粗限流保护节点,再由网关/Redis做可信用户全局配额。
四十五、学习验收
- 画出Key查找、时间衰减、超额计算、延迟和拒绝过程。
- 对rate=2r/s、burst=3逐请求推导时间线。
- 比较默认delay、nodelay和delay=1。
- 解释nodelay放行后为什么还要消化债务。
- 比较漏桶与令牌桶的突发行为,不说完全等价。
- 根据Key基数和长度设计zone并观察内存异常。
- 证明伪造XFF不能绕过可信真实IP限流。
- 解释NAT误伤并设计IP、账号和全局多层策略。
- 验证limit_conn在HTTP/1.1与HTTP/2并发请求下的差异。
- 扩容三个Nginx实例,解释本地额度如何放大。
- 为登录、短信、搜索、上传和Webhook分别设计策略。
- 使用dry run和状态变量完成上线前评估。
- 定位一次限流未生效、一次误伤和一次zone高基数问题。
- 写出全局限流服务故障时fail-open、fail-closed和本地降级方案。
