Nginx 不直接计算中位数,需通过 $upstream_response_time 采集后端耗时,配合 Prometheus/ELK/Loki 等外部工具实现响应时间中位数监控。

Nginx 本身不直接计算中位数,但可以通过标准变量 + 外部工具链实现“响应时间中位数”的准实时监控。关键在于:把原始耗时数据可靠采集出来,再交给擅长统计的系统处理。
用 $upstream_response_time 记录真实后端耗时
这是最核心的一步。该变量单位为秒(含毫秒精度),记录从 Nginx 向后端发起请求到收到响应头的时间,不含 Nginx 自身开销,适合横向对比后端性能。
- 多次重试时以逗号分隔,如
"0.012, 0.045" - 失败则对应位置为
-,如"0.018, -" - 配合
$upstream_addr可定位具体节点
在 http 块中定义专用日志格式:
log_format upstream_metrics '$remote_addr [$time_local] "$request" '
'$status $body_bytes_sent '
'"$upstream_addr" $upstream_response_time '
'$request_time';然后在 location 或 server 中启用:
access_log /var/log/nginx/upstream_metrics.log upstream_metrics;
✅ 这样每行日志就包含一次请求的后端地址与耗时,是后续计算中位数的原始依据。
将日志流实时接入支持分位数计算的监控系统
Nginx 日志是文本流,需借助外部工具做实时聚合。推荐两条成熟路径:
-
Prometheus + nginx-prometheus-exporter + Grafana
-
nginx-prometheus-exporter可解析 access log(需开启--enable-upstream-stats) - 它会暴露
nginx_upstream_response_msecs指标(直方图类型) - 在 Grafana 中用
histogram_quantile(0.5, sum(rate(nginx_upstream_response_msecs_bucket[5m])) by (le))即可算出 5 分钟窗口内的响应时间中位数(单位毫秒)
-
-
ELK(Logstash + Elasticsearch + Kibana)或 Loki + Grafana
- Logstash 用
dissect或grok提取$upstream_response_time字段并转为数值 - Elasticsearch 索引中对该字段开启
histogram聚合,Kibana 里用Percentiles可视化中位数 - Loki 配合 Promtail 抓取日志,Grafana 使用
| line_format "{{.upstream_response_time}}" | unwrap .upstream_response_time+quantile_over_time(0.5, ...)实现类似效果
- Logstash 用
⚠️ 注意:
$upstream_response_time是字符串,若含-或多个值(如重试),需在采集端做过滤或拆分。建议先用map或 Lua(OpenResty)预处理成单值(如取首次成功耗时)。
(可选)轻量级脚本方式:每分钟滚动计算中位数
适用于测试环境或临时验证,不建议用于高流量生产场景:
- 用
awk提取有效耗时(跳过-,取第一个非空值):awk '$12 != "-" && $12 > 0 {print $12}' /var/log/nginx/upstream_metrics.log | sort -n | awk '{ a[NR]=$1 } END { if (NR%2==1) print a[int(NR/2)+1]*1000 "ms" else print (a[NR/2]+a[NR/2+1])/2*1000 "ms" }' - 配合
cron每分钟执行,结果写入临时文件或推送到 Redis,供简单看板读取
? 提示:该方式延迟约 1 分钟,且未去重、未按接口路径区分。如需按
/api/user/api/order分别看中位数,必须在日志中加入$request_uri并在聚合时分组。
不复杂但容易忽略的是数据清洗和维度对齐。只要 $upstream_response_time 准确落盘,并匹配上 $upstream_addr 和 $request_uri,中位数就只是工具链上的一个函数调用。


















