需为每个upstream节点配置独立结构化日志,通过map映射backend_id、log_format嵌入$upstream_cache_status和$upstream_status等变量,并用awk脚本实时统计各节点缓存命中率与http/上游错误率。

要实时统计各后端节点的缓存命中率和错误率分布,不能只依赖全局日志或 stub_status 页面——必须把指标落到每个 upstream 节点上,并通过 Nginx 原生变量与日志结构化实现精准打点。核心思路是:用独立日志分离节点行为 + 关键变量埋点 + 轻量脚本聚合分析。
一、为每个节点生成带标识的访问日志
不分离日志,就无法区分是 node-a 慢还是 node-b 错误多。关键在 map + $upstream_addr + access_log 动态路径:
- 在 http 块中定义 map,将后端地址映射为可读 ID:
map $upstream_addr $backend_id {
default "unknown";
"10.0.1.10:8080" "node-a";
"10.0.1.11:8080" "node-b";
"10.0.1.12:8080" "node-c";
} - 定义日志格式,包含缓存状态与响应状态:
log_format node_detail '$remote_addr - $remote_user [$time_local] ' '"$request" $status $body_bytes_sent ' '$upstream_cache_status $upstream_status ' '$request_time $upstream_response_time $backend_id'; - 在 location 中启用节点级日志:
access_log /var/log/nginx/backend/$backend_id-access.log node_detail; - 确保
/var/log/nginx/backend/目录存在且 Nginx 用户(如 www-data)有写权限
二、统计各节点缓存命中率(HIT/MISS/EXPIRED)
命中率 = HIT 数 / 总请求量。需对每个节点日志单独计算:
- 查 node-a 当前命中率(近 1 小时):
awk '$10=="\"HIT\"" {hit++} END{printf "%.2f%\n", hit/NR*100}' /var/log/nginx/backend/node-a-access.log - 批量统计所有节点(假设日志文件存在):
for f in /var/log/nginx/backend/*-access.log; do n=$(basename $f | cut -d'-' -f1); h=$(awk -v q='"HIT"' '$10==q {hit++} END{printf "%.0f", hit/NR*100}' $f 2>/dev/null); echo "$n: ${h:-0}%"; done - $upstream_cache_status 字段值包括:HIT(命中)、MISS(未命中)、EXPIRED(过期)、BYPASS(绕过缓存)等,全部参与分母统计
三、统计各节点错误率(5xx + upstream 失败)
错误率需从两个维度叠加看:HTTP 层($status) + 代理层($upstream_status):
- HTTP 错误率(5xx 占比):
awk '$9 ~ /^5[0-9]{2}$/ {err++} END{printf "%.2f%\n", err/NR*100}' /var/log/nginx/backend/node-b-access.log - 上游失败率(连接超时、502/503/504 等):
awk '$11 ~ /(502|503|504|timeout|error)/ {err++} END{printf "%.2f%\n", err/NR*100}' /var/log/nginx/backend/node-c-access.log - 特别注意:$upstream_status 是空格分隔的多个值(如“200 502”表示重试后成功),建议用
awk '{split($11,a," "); for(i in a) if(a[i]~/^(502|503|504|timeout|error)$/) cnt++} END{print cnt/NR*100}'更准确提取
四、轻量实时监控脚本示例
每分钟执行一次,输出各节点关键指标,支持简单告警:
- 脚本片段(保存为
/usr/local/bin/nginx-node-stats.sh):
#!/bin/bash
LOG_DIR="/var/log/nginx/backend"
for log in $LOG_DIR/*-access.log; do
[ ! -s "$log" ] && continue
node=$(basename $log | cut -d'-' -f1)
hit=$(awk '$10=="\"HIT\"" {c++} END{print c+0}' $log)
total=$(wc -l err5xx=$(awk '$9~/^5[0-9]{2}$/ {c++} END{print c+0}' $log)
echo "$node: HIT=$(printf %.1f $((hit*1000/total))e-3)% 5xx=$(printf %.1f $((err5xx*1000/total))e-3)%"
done - 加入 crontab 每分钟运行:
* * * * * /usr/local/bin/nginx-node-stats.sh >> /var/log/nginx/backend/realtime-stats.log - 当某节点 5xx > 5% 或 HIT curl 推送企业微信/钉钉告警
不复杂但容易忽略:所有统计都基于日志行,务必保证日志格式稳定、字段位置一致;$upstream_cache_status 和 $upstream_status 必须在 proxy_pass 后生效,location 中未走代理的请求(如静态资源直出)不会产生这些变量。











