vts模块本身不提供P99/P95延迟直出指标,因其仅暴露原始直方图桶(times/counts),需由nginx-vts-exporter转为Prometheus指标后,再通过histogram_quantile()函数实时计算;且必须启用vhost_traffic_status_filter_by_host on,否则域名维度延迟数据将混入default主机导致失真。

直接说结论:vts 模块本身不提供 P99/P95 延迟直出指标,必须配合 nginx-vts-exporter + 自定义 PromQL 计算才能拿到,且需确认 Nginx 已启用 vhost_traffic_status_filter_by_host on,否则延迟数据会混在默认虚拟主机里无法按域名拆分。
为什么 vts 模块返回的 JSON 里没有 p95/p99 字段?
vts 模块(ngx_http_vhost_traffic_status_module)暴露的是原始计数器和直方图桶(bucket),比如 "request_time": {"times": [0,1,2,5,10,20,50,100,200,500,1000], "counts": [1240, 892, 301, 156, 77, 32, 12, 5, 2, 1, 0]} —— 它只存每个毫秒区间内的请求数,不预计算分位数。
这意味着:
-
nginx-vts-exporter把这些times和counts转成 Prometheus 的nginx_vts_server_request_time_bucket和nginx_vts_server_request_time_count指标,但仍是原始桶数据 - P95/P99 必须由 Prometheus 运行
histogram_quantile()函数实时计算,不是静态值 - 若 Nginx 配置漏掉
vhost_traffic_status_filter_by_host on,所有域名的请求时间会全堆进server="default",导致分位数失真
如何让 Prometheus 正确抓取 vts 的 request_time 直方图?
关键不在 Nginx 端“开开关”,而在 exporter 启动参数和 Nginx 的 vts 配置是否对齐:
- Nginx 配置中必须包含
vhost_traffic_status_zone(全局)和vhost_traffic_status_filter_by_host on(推荐开启) -
nginx-vts-exporter启动时要指定-nginx.scrape_uri=http://nginx/status/format/json,且该 URI 返回的 JSON 中必须有request_time结构(验证方式:curl http://nginx/status/format/json | jq '.servers[].request_time') - 确保
nginx-vts-exporter版本 ≥ v0.10.3,旧版可能忽略request_time或解析失败 - Prometheus 的
scrape_configs中job_name名称要与后续 PromQL 标签匹配,例如用job="nginx-vts"就别在查询里写job="nginx"
写什么 PromQL 才能拿到准确的 P99 延迟(按域名)?
直接用 histogram_quantile(0.99, sum by (le, host) (rate(nginx_vts_server_request_time_bucket{job="nginx-vts"}[5m]))) 是常见错误——它没排除 host="default" 的脏数据,也没处理 le="" 这种非法桶。
正确写法要加三重过滤:
- 只取真实域名:
host=~"^[a-zA-Z0-9.-]+.[a-zA-Z]{2,}$" - 排除空 host 和 default:
host!="", host!="default" - 限定时间范围避免冷启动干扰:
rate(...[10m])比[5m]更稳
完整示例(Grafana 面板用):
histogram_quantile(0.99,
sum by (le, host) (
rate(nginx_vts_server_request_time_bucket{
job="nginx-vts",
host=~"[^ ]+\.[^ ]+",
host!="default"
}[10m])
)
)
注意:host 标签实际来自 Nginx 的 server_name,如果用了泛域名或 IP 访问,host 可能为空,此时得改用 upstream 或 filter 维度。
容易被忽略的性能与精度陷阱
直方图分位数不是“查表”,而是插值估算,以下情况会让结果漂移:
-
request_time的times数组粒度太粗(比如只有 [10,100,1000]),P99 会卡在 100ms 桶里,无法反映真实分布;建议补全到 [1,5,10,20,50,100,200,500,1000,2000] - 低流量域名下
count总量太少(如每分钟 histogram_quantile 插值误差可能达 ±300ms -
nginx-vts-exporter默认每 30 秒拉一次 Nginx 状态,若 Nginx reload 频繁,可能漏掉部分桶计数,建议调高-nginx.timeout并加-nginx.retry - Grafana 面板刷新间隔设为 15s,但 Prometheus 抓取间隔是 30s,会导致曲线“阶梯状”,应统一为 30s 或启用
step参数对齐
最麻烦的其实是 vts 模块编译——它不支持动态加载,Nginx 升级或换版本后,必须重新编译并确认 nginx -V 2>&1 | grep vts 输出里有模块路径,否则整个链路静默失效。


















