用漏桶状态约束请求速率
limit_req 把漏桶计算、红黑树查状态与队列回收组合起来,按配置的 key 管理请求速率。
相关概念
工程问题#
需要允许有限突发,又要限制持续请求速率;多 worker 还要共享同一个客户端或业务 key 的计数状态。
实际实现#
共享内存节点记录 excess 和 last;到达时按流逝时间减少 excess,再计入新请求。红黑树定位 key,队列帮助清理旧状态。
处理流程#
- 计算 key 并查找共享状态
- 按时间衰减超额请求数
- 对照 burst 与 delay 决定处理
- 更新状态并维护回收顺序
源码片段#
固定版本 · 原文摘录src/http/modules/ngx_http_limit_req_module.crelease-1.26.3 · L454–L473
454 excess = lr->excess - ctx->rate * ms / 1000 + 1000;455456 if (excess < 0) {457 excess = 0;458 }459460 *ep = excess;461462 if ((ngx_uint_t) excess > limit->burst) {463 return NGX_BUSY;464 }465466 if (account) {467 lr->excess = excess;468469 if (ms) {470 lr->last = now;471 }472473 return NGX_OK;ngx_http_limit_req_lookup
excess 随时间按 rate 衰减,并以 1000 为一个请求的定点单位计入本次请求;超过 burst 时返回 NGX_BUSY,允许记账时更新 excess 和 last。速率计算与最终延迟、拒绝、日志响应分别发生在不同环节。
来自已找到现有 key 的分支;共享锁、时间差异常修正、红黑树查找和队列维护在上下文,不是每个请求都进入实体 FIFO。
收益与取舍#
工程分析带来的收益
- 状态量无需保存每个历史请求
- 突发容量与延迟策略可以分别配置
付出的代价
- 工程分析:共享锁可能成为热点
- 状态存储有限,粒度由 key 的选择决定
可比较的选择
滑动窗口日志能保留请求的精确时间分布,但每个 key 需要更多存储。
版本与适用范围#
对应 limit_req;是否延迟由配置决定,nodelay 等设置会改变行为,不能把所有请求理解为进入实体 FIFO。
参考来源#
源码、官方文档与原始论文01
nginx · ngx_http_limit_req_lookup / ngx_http_limit_req_account
源码release-1.26.3src/http/modules/ngx_http_limit_req_module.c · ngx_http_limit_req_lookup / ngx_http_limit_req_account
相关实现与比较#
4 个相关案例Envoy 本地限流按单调时钟补充令牌,用原子更新协调同一桶的并发扣减。桶容量决定可积累的突发量。
附源码片段 · 通信与流量
Linux 公平调度将 EEVDF 的资格条件与虚拟截止时间结合,用增强红黑树搜索候选任务。
附源码片段 · 调度与资源分配
伙伴分配器把空闲物理页组织为不同 order 的连续块,并用空闲链表管理同阶块。
附源码片段 · 内存管理
hrtimer 通过 timerqueue 的红黑树维护到期顺序,并缓存最早到期节点。
附源码片段 · 调度与资源分配