登录
推荐 文章 Go 技术 课程 下载 专题 AI
首页 >  文章 >  linux

curl 怎么分别查看 DNS、连接和首字节耗时

来源:17golang原创

时间:2026-09-06 00:47:31 218浏览 收藏

只看 curl 的总耗时,只能知道“这次请求花了多久”,不能知道慢在 DNS、TCP 连接、TLS 握手、服务端计算还是响应传输。最实用的办法是用 --write-out(简写 -w)一次打印累计时间,再用相邻字段相减得到每个阶段的耗时。

要点速览
  • time_namelookuptime_connecttime_appconnecttime_starttransfertime_total 都是从请求开始计算的累计时间。
  • DNS、TCP、TLS 等阶段时间不能直接把累计字段当结果,要用相邻字段做差。
  • --write-out 配合 -o /dev/null 可以把正文丢弃,只保留适合日志记录的一行指标。
  • 代理、重定向、连接复用和单次网络抖动都会改变口径,定位服务端问题前要先排除这些因素。
curl 时间字段与解析连接握手首字节完整传输的静态关系图
图1:curl 时间字段与请求阶段的静态对应关系;字段是累计值,具体阶段耗时需要用相邻字段相减。

curl 的时间字段分别代表什么

先记住一个关键点:curl 的时间变量默认是“从本次传输开始到某个节点完成”的累计秒数,而不是每一段独立的秒数。官方手册把 time_namelookup 定义为名称解析完成时间,把 time_connect 定义为 TCP 连接完成时间;HTTPS 场景还可以用 time_appconnect 表示 TLS 握手完成时间。

字段它回答什么常见用法
time_namelookup从开始到名称解析完成多久观察 DNS 阶段的累计位置
time_connect从开始到 TCP 连接完成多久与前一字段相减得到 TCP 耗时
time_appconnect从开始到 TLS/SSH 握手完成多久HTTPS 中与连接时间相减
time_pretransfer开始传输前的准备完成多久覆盖握手及协议准备
time_starttransfer从开始到收到首字节多久观察服务端等待和首包延迟
time_total完整操作结束多久与首字节时间相减得到后续传输

所以标题中的“DNS、连接、首字节”分别对应 time_namelookup 的累计位置、time_connect 的累计位置,以及 time_starttransfer 的累计位置。它们不是三支互不相关的计时器,而是同一次请求上逐步增加的观察点。

怎样一次输出可比较的耗时数据

排查时建议把响应体丢到 /dev/null,同时保留错误信息和格式化指标。下面这条命令适合手工执行,也适合放进临时采样脚本:

curl -sS -o /dev/null \
  -w 'code=%{http_code} remote=%{remote_ip}\nDNS=%{time_namelookup}s TCP=%{time_connect}s TLS=%{time_appconnect}s pre=%{time_pretransfer}s first_byte=%{time_starttransfer}s total=%{time_total}s\n' \
  'https://example.com/health'
# -sS 保留错误信息;-o 丢弃响应体;-w 只输出本次传输的测量字段

-w 的格式字符串中,变量写成 %{变量名},换行写成 \n。输出中的 coderemote 不是耗时字段,但很重要:同一个域名可能解析到不同地址,状态码也能帮助你区分“服务端很快返回错误”和“服务端迟迟没有首字节”。

HTTP 明文请求没有 TLS 握手,time_appconnect 可能是零;不要因此把它当成一次失败。HTTPS 请求则要把证书和加密握手包含在连接分析里。为了让多次结果容易比较,尽量固定 URL、代理设置和请求方法,连续采样几次再看分布。

用字段差值判断慢在哪里

拿到一行输出后,按照下面的关系把累计值还原成阶段值:

DNS耗时 = time_namelookup
TCP耗时 = time_connect - time_namelookup
TLS耗时 = time_appconnect - time_connect
准备耗时 = time_pretransfer - time_appconnect
服务端等待 = time_starttransfer - time_pretransfer
响应传输 = time_total - time_starttransfer
# 每一行都使用相邻累计节点,避免把累计时间重复相加

例如 time_namelookup 很小但 time_connect - time_namelookup 很大,优先检查网络路径、代理或目标端口,而不是先改 DNS。若连接阶段正常,time_starttransfer - time_pretransfer 却明显偏大,才更像服务端排队、数据库查询或应用计算拖慢首包。首字节已经很快、但 time_total - time_starttransfer 很大,则应转向响应体大小、带宽和接收窗口排查。

curl 累计时间字段通过相减得到 DNS TCP TLS 服务端等待和响应传输耗时的关系图
图2:用相邻累计字段相减得到可排查的阶段耗时;右侧结果只表示测量口径,不是固定性能阈值。

结果异常时先排除四个测量边界

代理会改变连接的解释

如果环境设置了 HTTPS_PROXY 或显式使用 --proxytime_connect 记录的可能是到代理的 TCP 连接,而不是到最终业务服务器的直连时间。文章里的差值仍然有效,但结论应写成“客户端到代理慢”或“代理之后慢”,不要直接归因于源站。

重定向会让一次请求包含多个 URL

加上 -L 后,curl 会跟随重定向。此时总耗时可能包含多个解析、连接和响应阶段,最后一组 remote_ip 也可能不是初始地址。要定位首个跳转,先不加 -L 看响应头,再分别测最终 URL。

连接复用会让后续请求看起来更快

同一次 curl 命令里请求多个 URL 时,curl 会尝试复用连接;但不同的 curl 进程之间不能复用连接。因此“同一命令第二次很快”与“单独启动一次 curl 很快”不是一个实验。压测或巡检要明确是否要测冷连接,并记录 num_connects 作为辅助信息。

单次读数不能代替连续采样

DNS 缓存、TCP 拥塞、代理负载和服务端排队都可能造成偶发尖峰。可以把格式字符串放入循环,每次记录时间戳、状态码、远端地址和五个时间字段,然后比较中位数与高分位,而不是只拿某一次最大值下结论。

常见问题

只想看首字节耗时,最少要输出哪个字段?

输出 %{time_starttransfer} 即可,但它仍是从请求开始算的累计时间。要看服务端从“准备完毕”到首字节的等待,应计算 time_starttransfer - time_pretransfer

为什么 DNS 耗时和 time_namelookup 不是总能对上?

在单 URL、没有特殊代理和重定向的简单请求中,二者可以近似对应;遇到 DoH、代理、连接复用或多次传输时,实际解析路径和统计边界会变化,应结合完整输出判断。

curl 输出的 total 比各段直接相加小,正常吗?

正常。累计字段之间存在重叠,正确做法是使用相邻字段相减;不要把所有累计字段相加,否则会重复计算。

time_namelookuptime_connecttime_appconnecttime_pretransfertime_starttransfertime_total 一起记录,再用相邻值做差,curl 就不只是“请求是否成功”的工具,也能成为一把边界清楚的网络排查尺。

声明:本文转载于:17golang原创 如有侵犯,请联系study_golang@163.com删除
相关阅读
更多>
最新阅读
更多>
课程推荐
更多>