LOGO 首页 OA教程 ERP教程 模切知识交流 PMS教程 CRM教程 技术文档 其他文档  
 
网站管理员

Nginx 请求很慢、后端却很快?先检查这 6 个地方

admin
2026年7月19日 14:4 本文热度 35

Nginx 请求时间为什么远大于上游响应时间

用户等了 8.2 秒,上游却只用了 120 毫秒,剩下的时间真是 Nginx 吃掉的吗?这篇文章用一条时间轴、增强日志和三组实验讲清楚。

先看一个常见的模拟故障场景。

用户说:“这个接口等了 8 秒多。”

Nginx 日志也确实显示:


request_time=8.200
upstream_response_time=0.120

后端团队一看,接口只处理了 120 毫秒,于是说:“应用没问题,剩下 8 秒肯定耗在 Nginx。”

网关团队觉的 Nginx 不可能凭空吃掉 8 秒,客户端则坚持用户确实等了 8 秒。三方的数据都可能是真的,问题出在大家拿了不同的秒表。

先说结论:这两个时间根本不是一个范围

Nginx 官方对 $request_time 的定义是:从读取到客户端发来的第一个字节开始,到最后一个响应字节发送给客户端、准备写入访问日志为止。

它覆盖的是用户请求在 Nginx 里经历的完整生命周期。

而 $upstream_response_time 记录的是 Nginx 从上游接收响应所花的时间。它描述的是上游交互阶段,不包含完整的客户端侧链路;同时,它也不应该被简单理解成“应用代码执行时间”。连接、传输、重试等因素都可能影响它。

把一次只有单个上游、没有内部跳转的请求简化成时间轴,大致是这样:


客户端开始上传请求
    │ T0:Nginx 读到第一个字节
    │
    │ 读取请求头和请求体、限流、鉴权、路由……
    │ T1:请求可以发往上游
    │
    │ 连接上游
    │ T2:连接建立
    │
    │ 等待并接收响应头
    │ T3:收到完整响应头
    │
    │ 接收上游响应体
    │ T4:收完上游响应
    │
    │ 缓冲、过滤、限速、发送给客户端
    │ T5:最后一个响应字节发送完成,写访问日志

request_time              ≈ T5 - T0
upstream_response_time    覆盖上游交互阶段,结束于 T4 附近

这只是概念图。遇到重试、子请求、缓存或内部跳转,链路会复杂得多。

所以,下面这个公式是错的:


Nginx 自身耗时 = request_time - upstream_response_time

这个差值最多只能叫“尚未解释的边缘链路时间”,可能来自客户端上传、Nginx 处理、响应缓冲或客户端接收。

第一种情况:请求还没到后端,时间已经过去了

最容易制造“8.2 秒对 120 毫秒”的,是慢上传。

假设客户端上传一个 8MB 的请求体,实际上传速度只有 1MB/s。光把数据送到 Nginx,就需要大约 8 秒。Nginx 收完后把请求交给上游,上游 120 毫秒处理完成。

最终你就可能看到:


request_time≈8.2s
upstream_response_time≈0.12s

这 8 秒既不是后端业务耗时,也不是 Nginx 在“计算”。大部分时间只是 Nginx 在等待客户端把请求发完。

为什么上传阶段特别容易被隐藏?因为 proxy_request_buffering 默认是 on。Nginx 会先读取完整请求体,再把请求发给代理服务器。只看后端日志,自然看不到前面的慢上传。

遇到这种情况,先看三个信号:

  • 请求方法是不是 POST、PUT 或 PATCH;
  • $request_length 是否明显偏大;
  • 慢请求是否集中在移动网络、跨地域客户端或上传接口。

先别急着看 Nginx CPU,要问请求什么时候真正到达上游。

第二种情况:上游早就返回了,客户端还没收完

反方向也一样。

假设上游 200 毫秒生成了一个 5MB 响应,而客户端只能以 100KB/s 接收。上游很快,用户却可能等待几十秒。

proxy_buffering 默认也是 on。开启响应缓冲后,Nginx 会尽快从上游读取响应,先放进内存缓冲区;放不下时,还可能写入临时文件。随后,Nginx 再按照客户端实际接收能力慢慢发送。

于是出现一个很容易误判的现象:

  • $upstream_response_time 很短:Nginx 已经迅速收完上游响应;
  • $request_time 很长:客户端迟迟没有收完;
  • $bytes_sent 或 $body_bytes_sent 很大:慢请求集中在大响应接口。

如果还配置了 limit_rate,或者上游通过 X-Accel-Limit-Rate 要求限速,差值会更明显。

关掉 proxy_buffering 后,响应会更同步地转发。慢客户端产生的背压可能传到上游,$upstream_response_time 反而变长。这通常是计时边界变化,判断时还要结合缓冲配置、响应大小和客户端网络。

第三种情况:进入上游前后,还有很多事情要做

Nginx 并不总是“收到请求后立刻 proxy_pass”。

请求在进入业务上游之前,可能还经历:

  • limit_req 带来的延迟;
  • 访问控制和鉴权子请求;
  • rewrite、location 匹配与内部跳转;
  • WAF、Lua、njs 或其他扩展模块处理;
  • 请求体落临时文件、磁盘或系统资源竞争。

上游响应结束之后,也可能继续经历压缩、过滤、缓存写入、限速以及向慢客户端发送。

这些时间都会影响完整的 $request_time,却不一定体现在你正在观察的那一个业务上游时间里。

所以差值大时,正确问题不是“Nginx 为什么慢”,而是:

在请求进入上游之前、上游返回之后,分别还经过了哪些阶段?

第四种情况:你看到的 upstream 时间可能不止一个

这是日志分析脚本最容易踩的坑。

如果第一次连接上游失败,Nginx 又重试了另一台服务器,日志可能出现:


upstream_addr=10.0.0.11:8080, 10.0.0.12:8080
upstream_status=502, 200
upstream_response_time=0.301, 0.120

逗号表示一次请求处理过程中联系了多个上游。发生由 X-Accel-Redirect 或 error_page 引起的内部跳转时,不同上游组还可能使用冒号分隔。

这时 $upstream_response_time 已经不是一个可以直接相减的数字,而是一组与 $upstream_addr$upstream_status 按位置对应的值。

如果监控平台强行把字符串取第一个、取最后一个,或者当成普通小数解析,结论都会失真。

比较时间之前,先确认三件事:

  1. 这次请求联系了几个上游;
  2. 每一次尝试的地址、状态码和时间能否一一对应;
  3. 是否发生内部跳转或错误重定向。

还有几类边界值,别急着报警

如果请求命中代理缓存,或者本来就是 Nginx 提供的静态资源,请求可能根本没有访问业务上游。此时 upstream 相关变量可能是 -,但 $request_time 仍然有值。

如果状态码是 499,通常意味着客户端在响应完成前断开;408 则常与客户端没有及时发送完整请求有关。这两类请求的时间更不能直接归因给后端。

如果没有选出可用的上游,$upstream_status 可能记录 502,其他 upstream 时间字段则可能没有正常值。

因此,监控平台至少要把 -、单值、多值和不同状态码分开处理。不要把 - 转成 0 后,再得出“Nginx 白白消耗了全部时间”的结论。

一份够用的增强日志配置

只记录 $request_time 和 $upstream_response_time 不够。排查慢请求时,至少要把连接、首包、完整响应、大小、状态和地址放在同一行里。


log_format timing_json escape=json
'{'
'"time":"$time_iso8601",'
'"request_id":"$request_id",'
'"remote_addr":"$remote_addr",'
'"method":"$request_method",'
'"uri":"$request_uri",'
'"status":"$status",'
'"request_length":"$request_length",'
'"bytes_sent":"$bytes_sent",'
'"request_time":"$request_time",'
'"upstream_addr":"$upstream_addr",'
'"upstream_status":"$upstream_status",'
'"upstream_connect_time":"$upstream_connect_time",'
'"upstream_header_time":"$upstream_header_time",'
'"upstream_response_time":"$upstream_response_time",'
'"upstream_bytes_sent":"$upstream_bytes_sent",'
'"upstream_bytes_received":"$upstream_bytes_received",'
'"upstream_cache_status":"$upstream_cache_status"'
'}';

access_log /var/log/nginx/access_timing.log timing_json;

这里把时间字段也写成字符串,是为了在变量值为 - 或多值列表时,日志仍然是合法 JSON。

如果后端还没有统一的链路 ID,可以把 Nginx 生成的请求 ID 传给上游:


proxy_set_header X-Request-ID $request_id;

如果系统已经有 Trace ID,就沿用原有规范,不要再制造第二套关联字段。

配置变更后,先检查再平滑加载:


nginx -t
nginx -s reload

由 systemd、容器或 Kubernetes 管理的环境,应使用对应的配置检查与发布流程,不要绕过现有变更机制。

三个实验,十分钟看懂时间花在哪里

以下命令中的 example.com 需要替换为隔离测试环境的实际地址。

实验一:制造慢上传

准备一个测试文件,并限制上传速度:


ddif=/dev/zero of=/tmp/slow-upload.bin bs=1M count=2
curl-sS --limit-rate 200k \
  --data-binary @/tmp/slow-upload.bin \
  https://example.com/upload -o /dev/null

在 proxy_request_buffering on 下,重点观察 $request_length$request_time 和上游时间。你会发现,上传等待主要进入完整请求时间,而上游仍可能很快。

实验二:制造慢下载

找一个较大的测试响应,限制客户端下载速度:


curl-sS --limit-rate 100k \
  https://example.com/large-file -o /dev/null

在 proxy_buffering on 下,如果上游可以被快速读取,$upstream_response_time 会较短,而 $request_time 会随着客户端接收时间增长。

实验三:切换响应缓冲

在隔离的测试环境里,将同一接口的 proxy_buffering 从 on 改为 off,重复慢下载实验。

关闭缓冲后,响应会同步转发,慢客户端的背压可能让 upstream 时间也随之增长。这个实验最能说明:指标变化不一定代表系统变慢,也可能只是计时边界变了。

不要在生产环境直接切换缓冲做实验。大响应、慢客户端和临时文件策略都会影响连接占用与上游压力。

可截图:6 步判断慢请求发生在哪一段

面对“request_time 很大、upstream 很小”,按下面的顺序查:

  1. 先看状态码和 upstream 是否存在。 缓存命中、静态响应、499、408、502 要单独分类。
  2. 确认 upstream 是单值还是多值。 有重试或内部跳转时,先还原每一次尝试。
  3. 看请求与响应大小。 大请求体优先排查慢上传;大响应优先排查慢下载与缓冲。
  4. 拆上游阶段。 connect 高,关注连接与网络;header 高,关注首包和应用排队;response 与 header 差距大,关注响应体生成和传输。
  5. 检查缓冲与限速。 看 proxy_request_bufferingproxy_bufferinglimit_rate 以及上游响应头。
  6. 用请求 ID 串联证据。 对齐 Nginx、应用日志和 APM,而不是拿不同请求的聚合平均值互相证明。

如果只有一个上游值,可以暂时计算:


edge_gap = request_time - upstream_response_time

但请把它命名为“未解释时间”或“边缘链路差值”,不要叫“Nginx 耗时”。它是排查入口,不是责任判决书。

技术管理者真正该改的,是监控口径

很多性能争议不是系统没有数据,而是数据的边界不同。

客户端看的是用户等待时间,Nginx 看的是代理生命周期,上游应用看的是自身代码或框架处理时间。三者都叫“响应时间”,却不是同一个指标。

更合理的监控方式,是把链路至少拆成三段:

  • 客户端接入段:请求上传、网络质量、客户端中断;
  • 网关与代理段:鉴权、限流、路由、缓冲、过滤与发送;
  • 上游服务段:连接、首包、完整响应和应用内部处理。

再用同一个请求 ID,把访问日志、应用日志和 Trace 串起来。

这样,事故会上不需要再争论“8 秒是不是 Nginx 吃掉的”。团队可以直接回答:8 秒发生在哪一段、由什么证据证明、下一步由谁处理。

最后记住一句话:

指标的名字不重要,计时从哪里开始、在哪里结束,才决定它能证明什么。

你见过哪一种:“慢上传”“慢下载”,还是“第一次 upstream 失败后重试”?欢迎留言说说最后是谁背了这口锅。


如果团队还在用“request_time 减 upstream”划分责任,可以把这篇文章发给一起排障的人。

来源

  • Nginx 官方文档:Module ngx_http_log_module — $request_time:https://nginx.org/en/docs/http/ngx_http_log_module.html#var_request_time
  • Nginx 官方文档:Module ngx_http_upstream_module — upstream 变量:https://nginx.org/en/docs/http/ngx_http_upstream_module.html#variables
  • Nginx 官方文档:Module ngx_http_proxy_module — proxy_request_buffering:https://nginx.org/en/docs/http/ngx_http_proxy_module.html#proxy_request_buffering
  • Nginx 官方文档:Module ngx_http_proxy_module — proxy_buffering:https://nginx.org/en/docs/http/ngx_http_proxy_module.html#proxy_buffering
  • Nginx 官方文档:Module ngx_http_core_module — limit_rate:https://nginx.org/en/docs/http/ngx_http_core_module.html#limit_rate


阅读原文:点击这里


该文章在 2026/7/19 14:04:46 编辑过
关键字查询
相关文章
正在查询...
点晴ERP是一款针对中小制造业的专业生产管理软件系统,系统成熟度和易用性得到了国内大量中小企业的青睐。
点晴PMS码头管理系统主要针对港口码头集装箱与散货日常运作、调度、堆场、车队、财务费用、相关报表等业务管理,结合码头的业务特点,围绕调度、堆场作业而开发的。集技术的先进性、管理的有效性于一体,是物流码头及其他港口类企业的高效ERP管理信息系统。
点晴WMS仓储管理系统提供了货物产品管理,销售管理,采购管理,仓储管理,仓库管理,保质期管理,货位管理,库位管理,生产管理,WMS管理系统,标签打印,条形码,二维码管理,批号管理软件。
点晴免费OA是一款软件和通用服务都免费,不限功能、不限时间、不限用户的免费OA协同办公管理系统。
Copyright 2010-2026 ClickSun All Rights Reserved  粤ICP备13012886号-2  粤公网安备44030602007207号