使用 Linux Shell 脚本定时监控链接的响应时间

近期 Ricky 有一个监控链接响应时间的需求,需要测试某个站点的接口在

  1. 使用内网 IP 地址直接访问
  2. 经过内网 DNS
  3. 经过内网 DNS 和内网负载均衡
  4. 经过外网 DNS 、外网负载均衡和 https

时响应时间上的区别;以及在不同时间段内响应时间的区别;以及 KVM 虚拟机和物理服务器在响应时间上的区别。

KVM 虚拟机上的操作系统为 CentOS Linux 5.4 ,物理服务器上的操作系统为 CentOS Linux 6.2 。

监控命令说明:

监控命令主要是这几句:

W_PARAMETER='REDI:%{time_redirect}s - DNS:%{time_namelookup}s - CONN:%{time_connect}s - APP_CONN:%{time_appconnect}s - PRE_TRAN:%{time_pretransfer}s - START_TRAN:%{time_starttransfer}s - TOTAL:%{time_total}s\n'
TIME=$(date "+%F %H:%M:%S")
LOG_FILE=/root/monitor_link.log

echo $TIME - 192.168.1.1 - webcheck - `curl -m 2 -o /dev/null -s -w "$W_PARAMETER" http://192.168.1.1/webcheck.htm` >> $LOG_FILE

这里解释说明一下使用到的 curl 命令:

(1)curl 命令:

W_PARAMETER='REDI:%{time_redirect}s - DNS:%{time_namelookup}s - CONN:%{time_connect}s - APP_CONN:%{time_appconnect}s - PRE_TRAN:%{time_pretransfer}s - START_TRAN:%{time_starttransfer}s - TOTAL:%{time_total}s\n'

curl -m 2 -o /dev/null -s -w "$W_PARAMETER" http://192.168.1.1/webcheck.htm
  • -m 2 :设置最大传输时间,单位是秒;
  • -o /dev/null :屏蔽原有输出信息;
  • -s :silent 模式,不输出任何东西;
  • -w “$W_PARAMETER“:控制额外输出。

(2)curl 命令使用到的计时器说明(即 W_PARAMETER 这个变量里存储的字符串):

  • time_namelookup:从起始至域名解析完成的耗时;
  • time_connect:从起始至建立到 Web 服务器的 TCP 连接的耗时;
  • time_appconnect:从起始至建立到 Web 服务器的应用层( SSL )连接 / 握手完成的耗时;
  • time_pretransfer:从起始至准备开始传输数据的耗时;
  • time_starttransfer:从起始至收到 Web 服务器返回数据的第一个字节的耗时;
  • time_total:总的耗时;
  • time_redirect:整个过程中重定向的耗时。

一个 HTTP 请求,涉及多个阶段(下列 7 条与上面的 7 条基本对应):

  1. DNS 解析域名;
  2. 请求从 Clinet 路由至 Server,Clinet 与 Server 建立 TCP 连接;
  3. 如果使用了 HTTPS,还涉及 SSL 连接的建立;
  4. Server 开始准备数据(开始逻辑计算、调后端接口、查数据库缓存等);
  5. Server 开始传输数据(数据准备完成,开始给 Client 传数据);
  6. 数据传输完毕;
  7. 整个过程可能还涉及多次重定向。

一些简单的时间计算:

  1. TCP 建立连接的耗时 = time_connect – time_namelookup;
  2. 从建立 TCP 连接到 Server 返回 Client 第一个字节的时间 = time_starttransfer – time_connect;
  3. Server 处理数据的时间 = time_starttransfer – time_pretransfer;
  4. Client 接收数据的耗时(开始接收至接收完成)= time_total – time_starttransfer 。

echo 命令的作用是将输出的信息按照一定的格式写入到这个 /root/monitor_link.log 文本文件:

TIME=$(date "+%F %H:%M:%S")
LOG_FILE=/root/monitor_link.log

echo $TIME - 192.168.1.1 - webcheck - `curl ...(命令略)` >> $LOG_FILE

部署:

1、编写脚本,在命令行界面输入:

[root@host ~]# vi /root/monitor_link.sh

键入小写字母 i ,进入编辑模式,将以下脚本复制粘贴进去(请根据实际需要进行相应地修改):

#!/bin/bash

# curl 命令 -w 的参数
W_PARAMETER='REDI:%{time_redirect}s - DNS:%{time_namelookup}s - CONN:%{time_connect}s - APP_CONN:%{time_appconnect}s - PRE_TRAN:%{time_pretransfer}s - START_TRAN:%{time_starttransfer}s - TOTAL:%{time_total}s\n'
# 获取当前时间
TIME=$(date "+%F %H:%M:%S")
# 日志文件存放位置
LOG_FILE=/root/monitor_link.log

# /webcheck.htm 是一个静态文件
# /webservice 是需要测试的接口

#(1)192.168.1.1 为物理服务器,直接通过内网 IP 地址去访问静态文件
#(排除掉业务接口的干扰,仅测试单纯的 Web 服务器响应时间)
echo $TIME - 192.168.1.1 - webcheck - `curl -m 2 -o /dev/null -s -w "$W_PARAMETER" http://192.168.1.1/webcheck.htm` >> $LOG_FILE

#(2)www.test.local 为内网域名,该域名指向 192.168.1.1 这台物理服务器,通过内网域名去访问静态文件
#(排除掉业务接口的干扰,仅测试单纯的 Web 服务器响应时间 + 内网 DNS 域名解析的响应时间)
echo $TIME - www.test.local - webcheck - `curl -m 2 -o /dev/null -s -w "$W_PARAMETER" http://www.test.local/webcheck.htm` >> $LOG_FILE

#(3)192.168.1.1 为物理服务器,直接通过内网 IP 地址去访问需要测试的接口
echo $TIME - 192.168.1.1 - webservice - `curl -m 2 -o /dev/null -s -w "$W_PARAMETER" http://192.168.1.1/webservice` >> $LOG_FILE

#(4)www.test.local 为内网域名,该域名指向 192.168.1.1 这台物理服务器,通过内网域名去访问需要测试的接口
echo $TIME - www.test.local - webservice - `curl -m 2 -o /dev/null -s -w "$W_PARAMETER" http://www.test.local/webservice` >> $LOG_FILE

#(5)192.168.1.2 为 KVM 虚拟机,直接通过内网 IP 地址去访问需要测试的接口
echo $TIME - 192.168.1.2 - webservice - `curl -m 2 -o /dev/null -s -w "$W_PARAMETER" http://192.168.1.2/webservice` >> $LOG_FILE

#(6)f5.test.local 为内网域名,该域名指向 F5 硬件负载均衡设备,F5 再指向 192.168.1.2 这台 KVM 虚拟机 ,通过指向 F5 的内网域名去访问需要测试的接口
echo $TIME - f5.test.local - webservice - `curl -m 2 -o /dev/null -s -w "$W_PARAMETER" http://f5.test.local/webservice` >> $LOG_FILE

#(7)www.test.com 为外网域名,该域名先指向 A10 硬件负载均衡设备,A10 再指向 haproxy 代理服务器,haproxy 再指向 192.168.1.1 这台物理服务器,同时还要做 https 的加解密,通过外网域名去访问需要测试的接口
echo $TIME - https://www.test.com - webservice - `curl -m 2 -o /dev/null -s -w "$W_PARAMETER" https://www.test.com/webservice` >> $LOG_FILE
echo ========================================================================================================================================================================= >> $LOG_FILE

按一次 ESC 键退出编辑模式,然后键入 “ :wq ” 保存并退出。

2、为上述脚本赋予可执行权限,并创建日志文件:

[root@host ~]# chmod +x /root/monitor_link.sh
[root@host ~]# touch /root/monitor_link.log

3、让上述脚本每 30 秒钟自动运行一次,在命令行界面输入:

[root@host ~]# crontab -e

先键入大写字母 G ,跳到最后一行;再键入小写字母 o ,插入新的一行,并将以下命令复制粘贴进去:

* * * * * sleep 5; /root/monitor_link.sh
* * * * * sleep 35; /root/monitor_link.sh

如果需要按分钟来执行,只需要这么写就可以了:

# 每分钟执行一次
*/1 * * * * /root/monitor_link.sh

# 每 3 分钟执行一次
*/3 * * * * /root/monitor_link.sh

# 每 30 分钟执行一次
*/30 * * * * /root/monitor_link.sh

按一次 ESC 键退出编辑模式,然后键入 “ :wq ” 保存并退出。

因为 crontab 的最小时间单位是分钟,所以采用 sleep 的方式来实现秒级的运行。即同时创建两个自动任务:

  • 第一个任务每分钟执行一次,是先休眠 5 秒钟后再执行 monitor_link.sh ;
  • 另一个任务也是每分钟执行一次,是先休眠 35 秒钟后再执行 monitor_link.sh 。

这样就相当于只有一个任务,且每 30 秒钟执行一次。

至此,部署完成。

日志格式说明:

[root@host ~]# tail -11 monitor_link.log
=========================================================================================================================================================================
2018-06-29 22:10:31 - 192.168.1.1 - webcheck - REDI:0.000s - DNS:0.000s - CONN:0.001s - APP_CONN:0.000s - PRE_TRAN:0.001s - START_TRAN:0.006s - TOTAL:0.006s
2018-06-29 22:10:31 - www.test.local - webcheck - REDI:0.000s - DNS:0.006s - CONN:0.006s - APP_CONN:0.000s - PRE_TRAN:0.006s - START_TRAN:0.012s - TOTAL:0.012s
2018-06-29 22:10:31 - 192.168.1.1 - webservice - REDI:0.000s - DNS:0.000s - CONN:0.000s - APP_CONN:0.000s - PRE_TRAN:0.001s - START_TRAN:0.010s - TOTAL:0.011s
2018-06-29 22:10:31 - www.test.local - webservice - REDI:0.000s - DNS:0.006s - CONN:0.006s - APP_CONN:0.000s - PRE_TRAN:0.006s - START_TRAN:0.015s - TOTAL:0.016s
2018-06-29 22:10:31 - 192.168.1.2 - webservice - REDI:0.000s - DNS:0.000s - CONN:0.001s - APP_CONN:0.000s - PRE_TRAN:0.001s - START_TRAN:0.021s - TOTAL:0.022s
2018-06-29 22:10:31 - f5.test.local - webservice - REDI:0.000s - DNS:0.006s - CONN:0.006s - APP_CONN:0.000s - PRE_TRAN:0.006s - START_TRAN:0.034s - TOTAL:0.035s
2018-06-29 22:10:31 - https://www.test.com - webservice - REDI:0.000s - DNS:0.006s - CONN:0.006s - APP_CONN:0.151s - PRE_TRAN:0.151s - START_TRAN:0.175s - TOTAL:0.176s
=========================================================================================================================================================================
[root@host ~]#

日志格式为:时间 – IP 地址或域名 – webcheck 静态文件或 webservice 接口 – REDI 时间 – DNS 时间 – CONN 时间 – APPCONN 时间 – PRE_TRAN 时间 – START_TRAN 时间 – TOTAL 时间

其中:

  • REDI 时间是 time_redirect
  • DNS 时间是 time_namelookup
  • CONN 时间是 time_connect
  • APPCONN 时间是 time_appconnect
  • PRE_TRAN 时间是 time_pretransfer
  • START_TRAN 时间是 time_starttransfer
  • TOTAL 时间是 time_total

分析:

为了便于理解,先对上述脚本中的 7 个检测做一个简述:

  1. 测试机 → 物理服务器 192.168.1.1 → /webcheck.htm
  2. 测试机 → www.test.local → 物理服务器 192.168.1.1 → /webcheck.htm
  3. 测试机 → 物理服务器 192.168.1.1 → /webservice
  4. 测试机 → www.test.local → 物理服务器 192.168.1.1 → /webservice
  5. 测试机 → 虚拟机 192.168.1.2 → /webservice
  6. 测试机 → f5.test.local → F5 → 虚拟机 192.168.1.2 → /webservice
  7. 测试机 → www.test.com → A10 → haproxy → 物理服务器 192.168.1.1 → /webservice

上述 7 个检测的总延迟时间:

  1. 总延迟 = 网络延迟 + 不跑业务代码的情况下物理服务器的处理延迟
  2. 总延迟 = 网络延迟 + DNS 处理延迟 + 不跑业务代码的情况下物理服务器的处理延迟
  3. 总延迟 = 网络延迟 + 物理服务器的处理延迟
  4. 总延迟 = 网络延迟 + DNS 处理延迟 + 物理服务器的处理延迟
  5. 总延迟 = 网络延迟 + KVM 物理主机的处理延迟 + 虚拟机的处理延迟
  6. 总延迟 = 网络延迟 + DNS 处理延迟 + F5 处理延迟 + KVM 物理主机的处理延迟 + 虚拟机的处理延迟
  7. 总延迟 = 网络延迟 + DNS 处理延迟 + A10 处理延迟 + haproxy 代理服务器的处理延迟 + 物理服务器的处理延迟

从 “ 日志格式说明 ” 中的那份日志输出可以看出:

1、这里需要先说明的是:

  • 如果请求没有使用到 DNS 域名解析,那么 DNS:0.000s;
  • 如果请求没有使用到 https,那么 APP_CONN:0.000s;
  • 如果请求没有使用到重定向,那么 REDI:0.000s 。

2、DNS 处理延迟大约为 6 毫秒( DNS:0.006s ),而且每次通过域名进行访问均要做一次 DNS 域名解析,如果使用 IP 地址直接访问则可以省下这 6 毫秒。

3、从第 1 条和第 2 条日志输出我们可以看到,从收到 Web 服务器返回数据的第一个字节到传输完成花费了 0 毫秒(以第 1 条为例:TOTAL:0.006s – START_TRAN:0.006s = 0.000s );而从第 3 、4 、5 、6 和 7 条日志输出我们可以看到,从收到 Web 服务器返回数据的第一个字节到传输完成均花费了 1 毫秒(以第 3 条为例:TOTAL:0.011s – START_TRAN:0.010s = 0.001s );因为静态文件 /webcheck.htm 里并没有存储几个字符,而接口 /webservice 返回的数据会比较多,所以花费的时间也多。

4、从第 1 条和第 3 条日志输出我们可以看到,第 1 条显示的总延迟为 6 毫秒( TOTAL:0.006s ),第 3 条显示的总延迟为 11 毫秒( TOTAL:0.011s ),那么访问接口 /webservice 比访问静态文件 /webcheck.htm 慢了大约 5 毫秒,毕竟在 Web 容器里跑业务接口的代码需要时间、将返回的数据传输到测试机也需要时间。

5、从第 3 条和第 5 条日志输出我们可以看到,第 3 条显示的总延迟为 11 毫秒( TOTAL:0.011s ),第 5 条显示的总延迟为 22 毫秒( TOTAL:0.022s ),那么虚拟机比物理服务器的总延迟慢了大约 11 毫秒。

6、从第 5 条和第 6 条日志输出我们可以看到,第 5 条显示的总延迟为 22 毫秒( TOTAL:0.022s ),第 6 条显示的总延迟为 35 毫秒( TOTAL:0.035s ),则第 6 条比第 5 条要慢了大约 13 毫秒,而这 13 毫秒的延迟中又有 6 毫秒是 DNS 处理延迟(第 5 条的 DNS 处理延迟为 DNS:0.000s ,第 6 条的 DNS 处理延迟为 DNS:0.006s ),那么剩下的 7 毫秒主要为 F5 硬件负载均衡设备的处理延迟。

7、从第 7 条日志输出我们可以看到,第 7 条的总延迟竟然高达 176 毫秒( TOTAL:0.176s ),而时间主要花费在了从 TCP 连接建立完成到应用层(SSL)连接 / 握手完成上,这段时间共花费了 145 毫秒( APP_CONN:0.151s – CONN:0.006s = 0.145 s );从应用层(SSL)连接 / 握手完成、准备开始传输数据至收到 Web 服务器返回数据的第一个字节共花费了 24 毫秒( START_TRAN:0.175s – PRE_TRAN:0.151s = 0.024 s )。

其他:

  • NAMELOOKUP:从开始计算,域名解析完成的耗时
    CURLINFO_NAMELOOKUP_TIME. The time it took from the start until the name resolving was completed.
  • CONNECT:从开始计算,TCP 建立完成的耗时
    CURLINFO_CONNECT_TIME. The time it took from the start until the connect to the remote host (or proxy) was completed.
  • APPCONNECT:从开始计算,应用层(SSL,在 TCP 之上的应用层)连接 / 握手完成的耗时
    CURLINFO_APPCONNECT_TIME. The time it took from the start until the SSL connect/handshake with the remote host was completed. (Added in in 7.19.0)
  • PRETRANSFER:从开始计算,准备开始传输数据的耗时
    CURLINFO_PRETRANSFER_TIME. The time it took from the start until the file transfer is just about to begin. This includes all pre-transfer commands and negotiations that are specific to the particular protocol(s) involved.
  • STARTTRANSFER:从开始计算,开始传输数据的耗时(libcurl 接收到第一个字节)
    CURLINFO_STARTTRANSFER_TIME. The time it took from the start until the first byte is received by libcurl.
  • TOTAL:总的耗时
    CURLINFO_TOTAL_TIME. Total time of the previous request.
  • REDIRECT:整个过程重定向的耗时,如果整个过程没有重定向,这个时间为 0
    CURLINFO_REDIRECT_TIME. The time it took for all redirection steps include name lookup, connect, pretransfer and transfer before final transaction was started. So, this is zero if no redirection took place.

这篇文章对你有帮助吗?

相关文章

发表评论?

此站点使用Akismet来减少垃圾评论。了解我们如何处理您的评论数据