最实用方式是OpenResty+Lua组合方案:利用ngx.shared.DICT共享内存与log_by_lua_block实现毫秒级秒级状态码统计,单机支撑10w+ QPS,配合定时清理和合理TTL避免内存泄漏。

要实现 Nginx 中 HTTP 状态码的秒级统计,不依赖外部存储(如 Redis、Prometheus),也不修改 Nginx 核心源码,最实用的方式是基于 nginx-module-vts(Nginx Virtual Host Traffic Status)进行定制增强,或使用 ngx_http_stub_status_module + 自定义共享内存 + Lua 脚本(OpenResty)组合方案。纯 C 模块开发门槛高、维护成本大,对多数业务场景属于过度设计。
用 OpenResty + Lua 实现轻量秒级状态码统计
OpenResty 内置 ngx.shared.DICT 共享内存字典,支持原子增减与 TTL,配合 log_by_lua_block 可在请求结束时实时汇总状态码,精度达毫秒级,实测单机可支撑 10w+ QPS 统计无压力。
- 在 nginx.conf 的 http 块中声明共享字典:lua_shared_dict status_sec 10m;
- 在 server 或 location 块中添加:log_by_lua_block { local dict = ngx.shared.status_sec; local key = os.date("%Y%m%d%H%M%S") .. ":" .. ngx.var.status; dict:incr(key, 1) }
- 通过 /status 接口暴露数据:用 content_by_lua_block 遍历 dict.keys(),按秒聚合返回 JSON,例如 {"20240520143022:200":128,"20240520143022:502":3}
复用 nginx-module-vts 并扩展时间粒度
vts 默认只提供分钟级聚合($vts_server_request_seconds_total),但其底层使用 slab 分配器管理共享内存,可通过 patch 其 src/ngx_http_vts_module.c,在 ngx_http_vts_get_upstream_statistics 函数中增加以秒为单位的 key 构造逻辑(如 "s:"..ngx_time()..":"..r->headers_out.status),再暴露新变量供 stub_status 或 API 输出。
- 编译时启用 vts 模块:./configure --add-module=../nginx-module-vts
- 配置中开启状态页:location /status { vhost_traffic_status_display; vhost_traffic_status_display_format html; }
- 关键改造点:避免每秒新建共享内存 slot,复用已有 slab page,仅变更 key 哈希策略与过期清理逻辑
避免常见性能陷阱
直接在 log_by_lua 中调用 os.time() 或 string.format 会产生 GC 压力;高频写共享字典未加锁可能引发竞争(但 incr 是原子的);未设置 dict TTL 会导致内存泄漏。
- 用 ngx.now() 替代 os.time(),用 string.format("%010d", math.floor(ngx.now())) 生成秒级时间戳
- 为 dict 设置合理 max_size 和过期时间,例如:lua_shared_dict status_sec 10m; # 实际内存占用约 3–5MB
- 定期清理过期 key:用 timer_at 机制每 5 秒扫描并删除早于当前时间 60 秒的 key(需 Lua-resty-timer 或自定义 cleanup handler)
不复杂但容易忽略:状态码统计必须在 log 阶段完成,不能放在 access_by_lua,因为后者无法捕获 upstream timeout 或 500 类错误的真实响应码;同时确保 error_log warn 级别以上不淹没统计日志。实际部署建议用 systemd 管理 OpenResty,并用 curl 定时拉取 /status 接口推送到 Grafana。



















