应使用 shared memory zone 统一存储状态码计数器,因各 worker 进程内存隔离导致默认统计不同步;需通过 lua 的 lua_shared_dict 或 c 模块的 ngx_shared_memory_add 创建共享区,用原子操作(如 dict:incr 或 ngx_atomic_fetch_add)安全更新,并提供 /status 等接口输出聚合结果。

要解决 Nginx 多 worker 进程间状态码统计不同步的问题,核心是用 shared memory zone 统一存放计数器,并确保所有 worker 都写入同一块内存区域——而不是各自维护本地变量。
为什么默认状态下状态码统计会不同步
每个 worker 进程独立运行、内存隔离。若在 handler 中用普通全局变量或模块静态变量统计 200、404、502 等状态码,各进程数据完全不互通。reload 后清零,监控拉取时看到的是单个 worker 的局部视图,总和不准确,趋势不可靠。
用自定义 zone 存储状态码计数器
需通过 Nginx 模块(如 C 模块或 Lua 模块)配合共享内存 zone 实现:
- 在配置中声明 zone:例如
lua_shared_dict status_stats 1m;(Lua 场景)或在 C 模块中调用ngx_shared_memory_add()注册名为status_zone的区域 - zone 大小需预估:每种状态码占一个 slot,100KB 可容纳数千个键值对;建议按预期状态码种类 × 2 倍余量分配
- 键名设计推荐使用固定字符串,如
"200"、"502_upstream",避免动态拼接导致 key 泄漏
安全更新计数器的关键操作
不能直接对共享内存地址做 ++,必须依赖原子或加锁机制:
- Lua 场景下用
dict:incr("200", 1),底层调用原子加法,线程/进程安全 - C 模块中:对整型字段用
ngx_atomic_fetch_add(&counter->val, 1);对需多步更新的结构体(如带时间戳的聚合),先获取 slab 分配的 slot,再用ngx_shmtx_lock()临界区保护 - 禁止在请求处理阶段 malloc 新结构体写入共享内存——所有数据必须来自预分配的 slab 或固定偏移布局
对外暴露统计结果
共享内存本身不可直读,需配套输出接口:
- 用
location /status+content_by_lua_block遍历lua_shared_dict所有 key,格式化为 JSON 返回 - C 模块可注册 handler,在
ngx_http_status_handler中遍历 zone 内哈希表,汇总后输出 plain/text 或 Prometheus 格式 - 注意:遍历时不要长期持有锁,应只读快照,避免阻塞其他 worker 的写入











