共享内存是Nginx多Worker下代理状态统计最高效方案,通过原子操作和预分配结构体实现零依赖实时聚合,需自定义模块注册zone并安全读写。

直接用共享内存存代理状态统计,是 Nginx 多 Worker 场景下最高效、零额外依赖的方案。它绕过网络调用和进程间序列化,所有 worker 读写同一块物理内存,天然适合实时聚合类指标(如上游响应时间、失败次数、重试计数等)。
明确共享内存用途与数据结构设计
代理状态统计不是通用缓存,重点在于可原子更新、低冲突、易遍历。避免存储复杂对象或指针,只保留基础数值型字段:
- 每个 upstream server 对应一个固定偏移结构体:含成功请求数、失败次数、累计响应毫秒、最近成功时间戳等字段
- 使用
ngx_atomic_t类型字段(如ngx_atomic_uint_t),支持无锁原子增减;时间戳类字段需配合自旋锁保护写入 - 整个 zone 按 server 数量预估大小,例如 10 个 upstream + 每个结构体 64 字节 → 至少预留 1MB,再加 20% 冗余
在配置中声明并初始化共享内存区
必须在 http 块中显式定义 zone,名称需唯一且便于模块识别:
http {
# 声明名为 upstream_stats 的共享内存区,大小 2m
upstream_stats_zone upstream_stats:2m;
<pre class="brush:php;toolbar:false;"># 或复用已有机制:通过 limit_conn_zone 伪用(不推荐)
# limit_conn_zone $upstream_addr zone=upstream_stats:2m;
}
注意:upstream_stats_zone 是自定义指令,需由你开发的模块注册;若使用第三方模块(如 nginx-module-vts 或自研 C 模块),则按其文档启用对应 zone 指令。Nginx 自身不提供开箱即用的“代理统计 zone”,必须由模块驱动。
在模块中安全读写共享内存
worker 初始化阶段获取地址,运行时用原子操作更新,关键逻辑需严格遵循生命周期规则:
- 在
init_worker阶段遍历cycle->shared_memory查找upstream_stats,校验shm.addr非空 - 写入失败计数:
ngx_atomic_fetch_add(&node->fail_count, 1);—— 无需锁,线程/进程安全 - 更新响应时间总和(非原子):
ngx_shmtx_lock(&shpool->mutex); node->total_time_ms += now - start; ngx_shmtx_unlock(&shpool->mutex); - 禁止在 handler 中 malloc/new 结构体存入共享内存;所有内存必须来自 slab 分配器或静态偏移布局
暴露统计结果供外部访问
共享内存本身不可直读,需配套输出接口。常见做法有三种:
- 通过
stub_status扩展:新增/upstream_statuslocation,C 模块遍历 zone 内节点,格式化为 JSON 输出 - 接入
lua_shared_dict桥接:用 Lua 模块定时从 C zone 同步关键指标到 lua dict,再由content_by_lua_block返回 - 对接 Prometheus:使用
nginx-module-prometheus,其采集器能自动读取符合规范的共享内存结构(需模块导出标准 metrics 接口)
reload 会清空共享内存,若需持久化趋势数据,应在 reload 前由主进程将当前统计快照写入磁盘或发往远端监控系统。











