核心是拆解时间、绑定上下文、分层归因:通过自定义nginx日志格式记录$request_id、$upstream_response_time等关键字段,结合awk快速分析慢请求,并按后端服务、网络连接、Nginx/客户端三类责任边界归因,再用$request_id关联下游日志下钻根因。

核心是拆解时间、绑定上下文、分层归因——不单看$request_time大不大,而是用日志字段把“谁慢、在哪慢、为什么慢”说清楚。
一、先让日志能说话:必须记录的关键字段
默认 combined 格式无法分析慢链路。需在 nginx.conf 中定义带时间与上下文的 log_format:
log_format slowtrace '$remote_addr - $remote_user [$time_local] "$request" $status $body_bytes_sent "$http_referer" "$http_user_agent" $request_id $request_time $upstream_response_time $upstream_connect_time $upstream_addr $upstream_status';
关键点:
- $request_id:Nginx 1.11.0+ 内置,32位唯一ID,用于跨服务串联日志
- $upstream_response_time:纯后端响应耗时(首字节时间),不是应用内部耗时,但能暴露最外层瓶颈
- $upstream_addr + $upstream_status:知道具体哪台机器、哪个端口、是否返回502/504
- 务必在 location 块中启用:access_log /var/log/nginx/slowtrace.log slowtrace;
二、用命令快速定位问题链路环节
不用导入系统,几条 awk 就能圈出异常模式:
- 查所有总耗时超 800ms 的请求,并显示请求路径和耗时:
awk '$NF > 0.8 {print $(NF-1), $NF, $7}' /var/log/nginx/slowtrace.log | sort -k2nr | head -20
(假设 $7 是 $request,$NF 是 $request_time) - 找“又慢又高频”的接口路径(排除静态资源):
awk '$NF >= 0.8 && $7 ~ /^\/api\// && $7 !~ /\.(js|css|png|jpg)$/ {print $7}' /var/log/nginx/slowtrace.log | sort | uniq -c | sort -nr | head -10 - 查某次请求中哪个 upstream 最拖后腿(结合逗号分隔的 $upstream_response_time 和 $upstream_addr):
awk '$8 ~ /,/ {n=split($8,a,","); m=split($9,b,","); if(n==m) for(i=1;i 0.5) print b[i], a[i]}' /var/log/nginx/slowtrace.log | sort -k2nr | head -10
三、按三类责任边界归因验证
仅看数字不够,要结合字段组合判断问题归属:
- 后端服务慢:$upstream_response_time 高(如 >0.6s)且接近 $request_time,同时 $upstream_status 多为 200 → 查对应服务日志、数据库慢查询、JVM GC 日志
- 网络或连接层问题:$upstream_connect_time 明显偏高(如 >300ms),但 $upstream_response_time 正常 → 检查 DNS 解析延迟、后端连接池是否耗尽、TCP 重传率、跨机房 RTT
- Nginx 或客户端侧瓶颈:$request_time 高(如 2.3s)但 $upstream_response_time 很低(如 0.004s)→ 聚焦 client_max_body_size 限制上传、proxy_buffering off 导致流式阻塞、SSL 握手慢、或客户端弱网丢包
四、关联 trace ID 下钻根因
$request_id 是轻量级全链路追踪的起点:
- 确保下游服务也记录该 ID(如 Spring Boot 中用 MDC 打印到日志)
- 在 ELK 或 Grafana 中,用 $request_id 关联 Nginx 日志与后端日志,对比“Nginx 收到请求时间” vs “后端开始处理时间” vs “后端返回响应时间”
- 若 Nginx 记录 $upstream_response_time = 1.2s,而后端日志显示自身处理仅 80ms,则剩余 1.12s 极可能来自网络延迟、中间代理或后端线程排队



















