通常HTTP的性能分析是通过浏览器的开发者工具进行查看,但这种方式只能通过图形页面进行查看。如果想做性能监控或者在命令行下分析,可以通过curl命令来统计各阶段的耗时。我们先来看看一个HTTP请求的过程:

Screen-Shot-2018-10-16-at-14.51.29-1.png
(图片来自cloudflare)

每次http请求经过这些过程: 客户端发起请求-->DNS解析-->TCP连接-->SSL等协议握手-->服务器处理-->内容传输-->完成

查看curl 命令的手册,curl命令支持以下阶段的时间统计:

  1. time_namelookup : 从请求开始到DNS解析完成的耗时
  2. time_connect : 从请求开始到TCP三次握手完成耗时
  3. time_appconnect : 从请求开始到TLS握手完成的耗时
  4. time_pretransfer : 从请求开始到向服务器发送第一个GET请求开始之前的耗时
  5. time_redirect : 重定向时间,包括到内容传输前的重定向的DNS解析、TCP连接、内容传输等时间
  6. time_starttransfer : 从请求开始到内容传输前的时间
  7. time_total : 从请求开始到完成的总耗时

我们常关注的HTTP性能指标有:

  1. DNS请求耗时 : 域名的NS及本地使用DNS的解析速度
  2. TCP建立耗时 : 服务器网络层面的速度
  3. SSL握手耗时 : 服务器处理HTTPS等协议的速度
  4. 服务器处理请求时间 : 服务器处理HTTP请求的速度
  5. TTFB : 服务器从接收请求到开始到收到第一个字节的耗时
  6. 服务器响应耗时 :服务器响应第一个字节到全部传输完成耗时
  7. 请求完成总耗时

注意: 如果想分析HTTP性能的瓶颈,不建议使用带有重定向的请求进行分析,重定向会导致建立多次TCP连接或多次HTTP请求,多次请求的数据混在一起,数据不够直观,因此 time_redirect 对实际分析意义不大。

其中的运算关系:

  1. DNS请求耗时 = time_namelookup
  2. TCP三次握手耗时 = time_connect - time_namelookup
  3. SSL握手耗时 = time_appconnect - time_connect
  4. 服务器处理请求耗时 = time_starttransfer - time_pretransfer
  5. TTFB耗时 = time_starttransfer - time_appconnect
  6. 服务器传输耗时 = time_total - time_starttransfer
  7. 总耗时 = time_total

用curl命令统计以上时间:

curl -w '\ntime_namelookup=%{time_namelookup}\ntime_connect=%{time_connect}\ntime_appconnect=%{time_appconnect}\ntime_redirect=%{time_redirect}\ntime_pretransfer=%{time_pretransfer}\ntime_starttransfer=%{time_starttransfer}\ntime_total=%{time_total}\n\n' -o /dev/null -s -L 'https://www.nixops.me/'

以上内容不够直观,curl -w参数支持模板,新建一个文件timing.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
time_total=%{time_total}\n

使用模板在执行一次:

curl -w "@timing.txt" -o /dev/null -s -L 'https://www.nixops.me/'

将上述功能生成脚本stat.sh

#!/bin/bash
#author : will
#website: https://www.nixops.me

Default_URL=https://www.nixops.me
URL=${1:-$Default_URL}

Result=`curl -o /dev/null -s $URL \
        -w \
        'time_namelookup=%{time_namelookup}
time_connect=%{time_connect}
time_appconnect=%{time_appconnect}
time_redirect=%{time_redirect}
time_pretransfer=%{time_pretransfer}
time_starttransfer=%{time_starttransfer}
time_total=%{time_total}
'`


declare $Result

curl_timing(){
    printf "\e[92mcURL Timing: \e[0m\n"
    for i in $Result
    do  
            IFS='='
            printf "\e[96m%18s \e[0m: %10s \n" $i
    done
}
stat_timing(){

    Result_TCP=`printf "%.6f" $(echo $time_connect - $time_namelookup |bc -l)`
    Result_TLS=`printf "%.6f" $(echo $time_appconnect - $time_connect |bc -l)`
    Result_Server=`printf "%.6f" $(echo $time_starttransfer - $time_pretransfer |bc -l)`
    Result_TTFB=`printf "%.6f" $(echo $time_starttransfer - $time_appconnect |bc -l)`
    Result_Transfer=`printf "%.6f" $(echo $time_total - $time_starttransfer |bc -l)`

    printf "\n\e[92mResource Timing: \e[0m\n"
    printf "\e[96m%18s \e[0m: %.6f \n" "DNS Lookup" $time_namelookup
    printf "\e[96m%18s \e[0m: %.6f \n" "TCP Connection" $Result_TCP
    
    if  [ `echo "$time_appconnect == 0"|bc` -eq 0 ]
    then
        printf "\e[96m%18s \e[0m: %.6f \n" "TLS Handshake" $Result_TLS
    fi

    printf "\e[96m%18s \e[0m: %.6f \n" "Server Processing" $Result_Server
    printf "\e[96m%18s \e[0m: %.6f \n" "TTFB" $Result_TTFB
    printf "\e[96m%18s \e[0m: %.6f \n" "Content Transfer" $Result_Transfer
    printf "\e[96m%18s \e[0m: %.6f \n" "Finish" $time_total
}

curl_timing
stat_timing 

执行一下:

# ./stat.sh https://www.baidu.com

cURL Timing: 
   time_namelookup :   0.004087 
      time_connect :   0.006480 
   time_appconnect :   0.022001 
     time_redirect :   0.000000 
  time_pretransfer :   0.022026 
time_starttransfer :   0.025635 
        time_total :   0.025658 

Resource Timing: 
        DNS Lookup : 0.004087 
    TCP Connection : 0.002393 
     TLS Handshake : 0.015521 
 Server Processing : 0.003609 
              TTFB : 0.003634 
  Content Transfer : 0.000023 
            Finish : 0.025658 

从上述结果,就可以直观的分析出http各阶段的耗时,方便我们进行性能瓶颈。

参考文章:

https://cizixs.com/2017/04/11/use-curl-to-analyze-request/
https://blog.cloudflare.com/a-question-of-timing/
https://curl.se/docs/manpage.html
https://github.com/reorx/httpstat