使用 curl 命令分析请求的耗时情况

最近工作中遇到一个问题,某个请求的响应特别慢,因此我就希望有一种方法能够分析到底请求的哪一步耗时比较长,好进一步找到问题的原因。

在网络上搜索了一下,发现了一个非常好用的方法,curl 命令就能帮助分析请求的各个部分耗时情况。

curl 参数

curl 命令提供了 -w 参数,这个参数在 manpage 是这样解释的:

-w, --write-out <format>
              Make curl display information on stdout after a completed transfer. The format is a string that may contain plain text mixed with any number of variables. The  format
              can  be  specified  as  a literal "string", or you can have curl read the format from a file with "@filename" and to tell curl to read the format from stdin you write
              "@-".

              The variables present in the output format will be substituted by the value or text that curl thinks fit, as described below. All variables are specified  as  %{vari‐
              able_name} and to output a normal % you just write them as %%. You can output a newline by using \n, a carriage return with \r and a tab space with \t.

它能够按照指定的格式打印某些信息,里面可以使用某些特定的变量,而且支持 \n、\t 和 \r 转义字符。提供的变量很多,比如 status_code、local_port、size_download 等等,这篇文章我们只关注和请求时间有关的变量(以 time_ 开头的变量)。

试用

$ curl -o /dev/null -s -w "time_connect: %{time_connect}\ntime_starttransfer: %{time_starttransfer}\ntime_nslookup:%{time_namelookup}\ntime_total: %{time_total}\n" "https://www.baidu.com"
time_connect: 0.009
time_starttransfer: 0.065
time_nslookup:0.007
time_total: 0.065

步骤

先往文本文件 curl.txt 写入下面的内容:

   time_namelookup:  %{time_namelookup}\n
      time_connect:  %{time_connect}\n
   time_appconnect:  %{time_appconnect}\n
     time_redirect:  %{time_redirect}\n
  time_pretransfer:  %{time_pretransfer}\n
time_starttransfer:  %{time_starttransfer}\n
                   ----------\n
        time_total:  %{time_total}\n

每个变量的解释如下:

time_namelookup:DNS 域名解析的时候,就是把 https://baidu.com 转换成 ip 地址的过程
time_connect:TCP 连接建立的时间,就是三次握手的时间
time_appconnect:SSL/SSH 等上层协议建立连接的时间,比如 connect/handshake 的时间
time_redirect:从开始到最后一个请求事务的时间
time_pretransfer:从请求开始到响应开始传输的时间
time_starttransfer:从请求开始到第一个字节将要传输的时间
time_total:这次请求花费的全部时间

示例

例子:

$ curl -w "@curl.txt" -o /dev/null -s -L  'http://www.baidu.com'
time_namelookup:  0.004
       time_connect:  0.015
    time_appconnect:  0.000
      time_redirect:  0.000
   time_pretransfer:  0.015
 time_starttransfer:  0.027
                    ----------
         time_total:  0.027

可以看到这次请求各个步骤的时间都打印出来了,每个数字的单位都是秒(seconds),这样可以分析哪一步比较耗时,方便定位问题。这个命令各个参数的意义:

-w:从文件中读取要打印信息的格式
-o /dev/null:把响应的内容丢弃,因为我们这里并不关心它,只关心请求的耗时情况
-s:不要打印进度条
从这个输出,我们可以算出各个步骤的时间:

DNS 查询:4ms
TCP 连接时间:pretransfter(15) - namelookup(4) = 11ms
服务器处理时间:starttransfter(27) - pretransfer(15) = 12ms
内容传输时间:total(27) - starttransfer(27) = 0ms

-w 参数详解

以下是 - w 参数对应的一些变量以及对应的解释:

  • url_effective 最终获取的 url 地址,尤其是当你指定给 curl 的地址存在 301 跳转,且通过 - L 继续追踪的情形。
  • http_code http 状态码,如 200 成功, 301 转向, 404 未找到, 500 服务器错误等。(The numerical response code that was found in the last retrieved HTTP (S) or FTP (s) transfer. In 7.18.2 the alias response_code was added to show the same info.)
  • http_connect The numerical code that was found in the last response (from a proxy) to a curl CONNECT request. (Added in 7.12.4)
  • time_total 总时间,按秒计。精确到小数点后三位。 (The total time, in seconds, that the full operation lasted. The time will be displayed with millisecond resolution.)
  • time_namelookup DNS 解析时间, 从请求开始到 DNS 解析完毕所用时间。(The time, in seconds, it took from the start until the name resolving was completed.)
  • time_connect 连接时间, 从开始到建立 TCP 连接完成所用时间, 包括前边 DNS 解析时间,如果需要单纯的得到连接时间,用这个 time_connect 时间减去前边 time_namelookup 时间。以下同理,不再赘述。(The time, in seconds, it took from the start until the TCP connect to the remote host (or proxy) was completed.)
  • time_appconnect 连接建立完成时间,如 SSL/SSH 等建立连接或者完成三次握手时间。(The time, in seconds, it took from the start until the SSL/SSH/etc connect/handshake to the remote host was completed. (Added in 7.19.0))
  • time_pretransfer 从开始到准备传输的时间。(The time, in seconds, it took from the start until the file transfer was just about to begin. This includes all pre-transfer commands and negotiations that are specific to the particular protocol (s) involved.)
  • time_redirect 重定向时间,包括到最后一次传输前的几次重定向的 DNS 解析,连接,预传输,传输时间。(The time, in seconds, it took for all redirection steps include name lookup, connect, pretransfer and transfer before the final transaction was started. time_redirect shows the complete execution time for multiple redirections. (Added in 7.12.3))
  • time_starttransfer 开始传输时间。在发出请求之后,Web 服务器返回数据的第一个字节所用的时间 (The time, in seconds, it took from the start until the first byte was just about to be transferred. This includes time_pretransfer and also the time the server needed to calculate the result.)
  • size_download 下载大小。(The total amount of bytes that were downloaded.)
  • size_upload 上传大小。(The total amount of bytes that were uploaded.)
    size_header 下载的 header 的大小 (The total amount of bytes of the downloaded headers.)
  • size_request 请求的大小。(The total amount of bytes that were sent in the HTTP request.)
  • speed_download 下载速度,单位 - 字节每秒。(The average download speed that curl measured for the complete download. Bytes per second.)
  • speed_upload 上传速度, 单位 - 字节每秒。(The average upload speed that curl measured for the complete upload. Bytes per second.)
  • content_type 就是 content-Type,不用多说了,这是一个访问我博客首页返回的结果示例 (text / html; charset=UTF-8);(The Content-Type of the requested document, if there was any.)
  • num_connects 最近的的一次传输中创建的连接数目。Number of new connects made in the recent transfer. (Added in 7.12.3)
  • num_redirects 在请求中跳转的次数。Number of redirects that were followed in the request. (Added in 7.12.3)
    redirect_url When a HTTP request was made without -L to follow redirects, this variable will show the actual URL a redirect would take you to. (Added in 7.18.2)
  • ftp_entry_path 当连接到远程的 ftp 服务器时的初始路径。The initial path libcurl ended up in when logging on to the remote FTP server. (Added in 7.15.4)
  • ssl_verify_result ssl 认证结果,返回 0 表示认证成功。( The result of the SSL peer certificate verification that was requested. 0 means the verification was successful. (Added in 7.19.0))