Nginx通过共享内存区(shared memory zone)实现Worker进程间全局共享状态,master进程创建并管理该内存,各worker通过mmap映射访问,配合原子操作与锁保障并发安全,支撑limit_req等全局限流功能。

在 Nginx 中,Worker 进程之间默认是相互隔离的,无法直接共享变量。但通过 shared memory zone(共享内存区),多个 Worker 可以协同完成全局限速、计数等任务,这是实现全局限流(如 limit_req)的核心机制。
共享内存区如何被 Worker 进程共用
Nginx 在 master 进程启动时,会调用 shm_open()(Linux)或 CreateFileMapping()(Windows)创建一块操作系统级的共享内存,并由所有 worker 进程 mmap 映射到各自地址空间。这块内存不随某个 worker 退出而销毁,生命周期由 master 管理。
- 每个 shared zone 需显式声明,例如:
limit_req_zone $binary_remote_addr zone=perip:10m rate=1r/s; -
zone=perip:10m表示名为perip、大小为 10MB 的共享内存区 - 所有 worker 进程在初始化时都会访问同一块内存,读写基于原子操作或加锁保护(如红黑树节点插入时使用自旋锁)
限速计数的具体数据结构与更新逻辑
Nginx 在共享内存中维护一个基于 slab allocator + rbtree 的结构:键(key)通常是哈希后的客户端标识(如 $binary_remote_addr),值包含时间戳和请求计数。每次请求到来时:
- Worker 计算 key 的 hash 值,在共享内存的 rbtree 中查找对应节点
- 若存在且未过期(基于滑动窗口或固定时间片),则原子递增计数并更新最后时间
- 若超出阈值(如
rate=1r/s对应每秒最多 1 次),当前请求被拒绝或延迟 - 若 key 不存在或已过期,则新建节点(需 slab 分配内存,受锁保护)
为什么不能只靠单个 worker 的局部变量
假设仅用每个 worker 内部的变量做计数:
- 4 个 worker 同时运行时,相同 IP 的请求可能被分发到不同 worker,导致实际 QPS 达到配置值的 4 倍
- 重启某个 worker 会导致其局部计数清零,破坏限速连续性
- 无法实现跨进程的突发控制(burst)、延迟队列(nodelay)等高级行为
共享内存使限速策略真正成为“全局一致”的服务治理能力。
实际部署要注意的关键点
共享内存不是万能的,需结合场景合理配置:
-
size 要足够:过小会导致 key 频繁淘汰(LRU 清理),影响精度;可参考
limit_req_status日志统计命中/未命中比例 - key 设计要合理:避免使用高基数字段(如完整 URL 或 User-Agent),否则 hash 冲突多、内存浪费大
-
注意锁竞争:高并发下大量写同一 key(如热点 IP)可能因自旋锁造成 CPU 上升,可考虑加 salt 分散(如
$binary_remote_addr$uri) - reload 不清空共享内存:Nginx reload 时 zone 保留,计数持续有效;只有 stop/start 才重置


















