线上最常见的一句反馈就是“接口慢”。但如果不把耗时拆开,这句话几乎没有排障价值。慢到底慢在 DNS、TCP 建连、TLS 握手、服务处理,还是响应体下载,必须拆出来才能继续判断。

HTTP 排障最容易犯的错误,是把总耗时当成唯一指标。真正有价值的是把总时间拆成多个阶段,然后看瓶颈究竟落在哪一步。

为什么 curl 特别适合做第一层判断

浏览器适合使用,但不适合做最小现场分析。curl 的价值在于它足够轻、足够可控,而且可以把请求过程拆成多个时间段输出出来。

一个最常用的命令如下:

1
curl -o /dev/null -s -w 'dns:%{time_namelookup} connect:%{time_connect} tls:%{time_appconnect} starttransfer:%{time_starttransfer} total:%{time_total}\n' https://example.com

这里几个字段非常重要。

  • time_namelookup 表示 DNS 解析完成时间。
  • time_connect 表示 TCP 建连完成时间。
  • time_appconnect 表示 TLS 握手完成时间。
  • time_starttransfer 表示收到首字节时间,也常被理解为 TTFB。
  • time_total 表示整个请求结束时间。

只要把这些时间拆开,很多“接口慢”的问题就已经不再模糊。

一次标准的分析方式

假设某个接口总耗时 2 秒,不要急着说后端慢,先看每段耗时分布。

如果 dns 很高,那就先排 DNS。

如果 connect 很高,那就先看 TCP 建连和链路质量。

如果 tls 很高,那就重点看证书、握手和协商过程。

如果前面都很快,但 starttransfer 很高,那更像是服务端处理、缓存未命中、回源慢或者中间代理等待。

如果首字节很快,但 total 很慢,那要继续看响应体是否过大、是否有流式返回、是否存在客户端接收瓶颈。

配合详细输出一起看

时间拆解之外,再配合 -v 看过程会更完整。

1
curl -v https://example.com

这里你可以观察到。

  • 实际连接的 IP。
  • 是否复用了已有连接。
  • 请求头和响应头。
  • 是否经过重定向。
  • TLS 握手的一些关键信息。

如果服务经过 CDN、反向代理或网关,响应头里还常常会带一些非常有价值的排障线索,比如缓存命中状态、边缘节点标识、上游处理时间等。

一条更贴近实战的排查链路

面对慢请求,我一般会先用下面这个组合。

1
curl -v -o /dev/null -s -w 'dns:%{time_namelookup} connect:%{time_connect} tls:%{time_appconnect} starttransfer:%{time_starttransfer} total:%{time_total}\n' https://example.com/api

如果发现 connecttls 都很快,而 starttransfer 明显偏高,就说明网络建连大概率没问题,应更多怀疑服务处理、缓存命中、回源、数据库或下游依赖。

如果 dns 明显偏高,就先去对比不同 DNS 结果。

如果 connect 明显偏高,再结合 sstcpdump 看连接阶段是否存在重传或等待。

如果 tls 明显偏高,就需要进一步看 HTTPS 握手细节。

这就是为什么 HTTP 排障不能只盯着一个总耗时数字。因为同样是“2 秒”,背后可能是完全不同的系统问题。

常见误判

第一个常见误判,是把 TTFB 慢直接等同于应用代码慢。实际上,负载均衡等待、反向代理排队、CDN 回源、上游链路拥塞,都可能让首字节变慢。

第二个常见误判,是用浏览器手感代替数据。页面觉得慢,不代表接口真的慢;接口慢,也不代表 DNS 或 TLS 慢。没有时间拆解,就很容易把问题归因错。

第三个常见误判,是忽略重定向和连接复用。有时第一次慢是因为握手和重定向,后续请求快很多;如果不看过程,很容易误以为服务本身抖动。

一个值得自己做的小实验

你可以对同一个站点连续发几次请求,并记录每一段时间。

1
2
3
curl -o /dev/null -s -w 'dns:%{time_namelookup} connect:%{time_connect} tls:%{time_appconnect} starttransfer:%{time_starttransfer} total:%{time_total}\n' https://example.com
curl -o /dev/null -s -w 'dns:%{time_namelookup} connect:%{time_connect} tls:%{time_appconnect} starttransfer:%{time_starttransfer} total:%{time_total}\n' https://example.com
curl -o /dev/null -s -w 'dns:%{time_namelookup} connect:%{time_connect} tls:%{time_appconnect} starttransfer:%{time_starttransfer} total:%{time_total}\n' https://example.com

然后观察。

  • 第一次和后两次相比,是否有明显差异。
  • 是哪一段时间差异最大。
  • 如果请求一个静态资源和一个动态接口,两者瓶颈分别在哪。

这个实验特别适合训练“先拆阶段,再下结论”的习惯。

小结

HTTP 请求耗时分析真正的关键,不是把请求发出去,而是把时间拆开。只要你能用 curl 把 DNS、建连、TLS、首字节和总耗时分开看,“接口慢”这类原本非常笼统的问题,就会开始变成结构化问题。排障最怕模糊,而时间拆解恰好就是把模糊打碎的第一步。