$request_time仅统计nginx接收首字节请求头到写入末字节响应至socket缓冲区的耗时,不含tcp/tls握手、客户端等待及网络传输延迟,反映的是nginx自身处理与内核缓冲区层面耗时。

要利用 $request_time 全量审计从客户端建立 TCP 连接到 Nginx 发完最后一个字节的总体耗时,需明确:该变量**不包含 TCP 握手、TLS 握手、请求头接收前的等待、以及响应体发送到客户端网卡后的网络延迟**——它只统计 Nginx 接收到**第一个字节请求头开始**,到**把最后一个响应字节写入系统 socket 缓冲区为止**的时间(单位:秒,精度为毫秒)。
理解 $request_time 的真实覆盖范围
$request_time 是 Nginx 内置变量,其计时起点是 内核完成三次握手、Nginx accept() 成功返回、且收到首个请求数据包的第一个字节时;终点是 Nginx 调用 write()/sendfile() 将响应最后一字节成功写入内核 socket 发送缓冲区后立即打点。它不感知客户端是否已接收、ACK 是否到达、TCP 重传或慢启动影响。
因此,它反映的是 Nginx **自身处理 + 内核协议栈收发缓冲区层面的耗时**,不是端到端“用户感知延迟”(后者需结合 $upstream_response_time、前端 RUM 或客户端打点)。
开启全量日志并结构化记录
在 log_format 中显式加入 $request_time,并确保日志格式可被下游(如 ELK、Loki、Prometheus + nginxlog exporter)解析:
- 推荐格式示例:log_format main '$remote_addr - $remote_user [$time_local] "$request" $status $body_bytes_sent "$http_referer" "$http_user_agent" $request_time $upstream_response_time $pipe';
- 务必启用
access_log /path/to/access.log main buffer=64k flush=1s;,避免日志延迟掩盖真实分布 - 若需更高精度(微秒级),需自行编译 Nginx 并启用
--with-http_realip_module和自定义时间戳模块,原生不支持
区分瓶颈类型:结合其他关键变量交叉分析
单看 $request_time 无法定位根因。必须与以下变量联动判断:
- 若 $request_time 高,但 $upstream_response_time ≈ 0:说明瓶颈在 Nginx 本地,可能是 rewrite 复杂、大量 subrequest、limit_req/limit_conn 触发排队、SSL 密钥计算(ECDSA/P-256 较快,RSA-2048 较慢)、或磁盘 IO(如 access_log 同步写、static file sendfile 阻塞)
- 若 $request_time ≈ $upstream_response_time:说明上游服务响应慢,Nginx 自身开销小,应聚焦 upstream 优化
- 若 $request_time > $upstream_response_time 且差值稳定 >100ms:可能受 client_max_body_size 限制触发临时文件写入、或 response body 过大导致 sendfile() 分片阻塞、或启用了 gzip_vary 且 CPU 不足
排除干扰:确保统计不受配置误用影响
以下配置会显著扭曲 $request_time 的业务含义,审计前必须核查:
- 禁用
underscores_in_headers on;(若开启,部分客户端 header 解析失败会导致 request 处理异常延长) - 确认未开启
proxy_buffering off;(关闭后 Nginx 会逐包转发 upstream 响应,$request_time可能被拉长,且失去缓冲削峰作用) - 检查
keepalive_timeout是否过短(频繁重建连接会增加平均$request_time,但非单次请求问题) - 避免在 log_format 中使用
$msec或$time_iso8601替代$request_time——它们是绝对时间戳,无法用于耗时分析











