通过 curl 命令判断网络连接的耗时瓶颈

警告
本文最后更新于 2023-12-25,文中内容可能已过时。

通过 curl 命令判断网络连接的耗时瓶颈

关于 curl 的基础用法,请看《curl》

参考博客:HTTPS 请求阶段分析 | 深入架构原理与实践Timing With Curl - Susam PalA Question of Timing

一个完整、无任何缓存、未复用连接的 HTTPS 请求需要经过以下几个阶段:DNS 域名解析、TCP 握手、SSL/TSL 握手、服务器处理、内容传输。

关于 HTTPS 的具体的执行流程图,请看《浅谈 SSL&TLS 工作原理》的HTTPS小节

以下是基于 TLS 1.2 的 HTTP 的图

我们把从发出一个请求到收到服务端的响应这个时间,叫 RTT(Round-Trip Time)往返时延。

如上图所示,一个 HTTPS 请求共需要 5 个 RTT = 1 RTT(域名解析)+ 1 RTT(TCP 握手)+ 2 RTT(SSL/TSL 握手)+ 1 RTT(HTTP 内容请求传输)。

一个 HTTP 请求共需要 3 个 RTT,HTTP 跟 HTTPS 相比,没有 SSL/TSL 握手

如果网络出现问题,导致一个 RTT 增加了 N ms,那么整个 HTTPS 请求就会增加 5N ms,举个例子,说明接口延迟的计算和影响:假设北京到美国洛杉矶的 RTT 延迟为 190 毫秒,我们从北京访问美国洛杉矶的一个 HTTPS 服务,整个交互过程时延计算是5*190+后端业务延时。其中,“5"代表的是 HTTPS 请求的 5 个环节,每个环节都需要一个 RTT 的时间。因为网络延迟始终会受到物理环境的制约,无论使用什么技术、如何优化,950ms 的指标将会很难突破,所以我们的优化策略也是以减少请求环节、链路优化、降低 SSL/TSL 计算量为主

HTTPS 请求的各个阶段可以使用 curl 命令进行详细的耗时分析,如下表所示,curl 提供了详细的耗时分析选项,这样我们就可以更准确地掌握每一个环节的消耗时间,进一步提升网络优化的效率和精度。

请求阶段 释义(单位秒)
time_namelookup 从请求开始到域名解析完成的耗时
time_connect 从请求开始到 TCP 三次握手完成耗时
time_appconnect 从请求开始到 SSL/TLS/SSH 握手完成的耗时,如果未采用 SSL/TLS,则此项为 0
time_pretransfer 从请求开始到向服务器发送第一个请求开始之前的耗时,这包括针对所涉及的特定协议的所有预传输命令和协商。
time_redirect 重定向时间,包括到内容传输前的重定向的 DNS 解析、TCP 连接、内容传输等时间,如果没有重定向,则此项为 0
time_starttransfer 从请求开始到即将接收(还没接收)服务器传过来的第一个字节前的时间,这个时间=time_pretransfer+ 服务器处理请求的耗时 + 一个 RTT。
time_total 从请求开始到完成的总耗时

业务常关注的性能指标有:域名解析请求耗时、TCP 建立耗时、TTFB(Time To First Byte,首字节时间)等,计算方式如下

耗时 说明
域名解析耗时 = time_namelookup 域名 NS 及本地 LocalDNS 解析耗时
TCP 握手耗时 = time_connect - time_namelookup 建立 TCP 连接时间
SSL/TSL 耗时 = time_appconnect - time_connect TLS 握手以及加解密处理
服务器处理请求耗时 = time_starttransfer - time_pretransfer 服务器处理这个请求消耗了多长时间,其实严格来说是一个 RTT+ 服务器处理请求的时间
TTFB = time_starttransfer - time_appconnect (从客户端的角度看整个请求过程)从服务端和客户端成功成功建立连接到开始到客户端收到第一个字节的耗时
总耗时 = time_total

简单的实践

对一个接口使用 curl 测试,测试命令如下

1
curl -w '\n 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' -o /dev/null -s 'https://xiashuo.xyz'

第一次测试,输出:

1
2
3
4
5
6
7
8
9
curl -w '\n 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' -o /dev/null -s 'https://xiashuo.xyz'

 time_namelookup=1.195716
 time_connect=1.248346
 time_appconnect=1.901162
 time_redirect=0.000000
 time_pretransfer=1.901252
 time_starttransfer=1.990214
 time_total=1.990437

https://xiashuo.xyz测试的结果进行分段统计,结果如下:

耗时 说明(单位秒) 占比
域名解析耗时 = time_namelookup 1.195716 60.07%
TCP 握手耗时 = time_connect - time_namelookup 0.05263 2.64%
SSL/TSL 耗时 = time_appconnect - time_connect 0.652816 32.80%
服务器处理请求耗时 = time_starttransfer - time_pretransfer 0.088962 4.47%
TTFB = time_starttransfer - time_appconnect 0.089052 4.47%
总耗时 = time_total 1.990437

可以很明显的看出,域名解析时间大约占据了总时间的 60%,SSL/TSL 层耗时则占据了总时间的近 32%,光这两项就耗费了差不多 90% 的时间,而 TCP 连接大约占据了总时间的 2.64%,几乎忽略不计。由此可见,如果我们想要降低 HTTPS 接口延迟,那么优化域名解析环节和 SSL/TSL 这两个阶段将会为我们带来显著的性能提升。

紧接着第一次测试结束,我们立即进行第二次测试

1
2
3
4
5
6
7
8
9
curl -w '\n 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' -o /dev/null -s 'https://xiashuo.xyz'

 time_namelookup=0.008063
 time_connect=0.052308
 time_appconnect=0.145729
 time_redirect=0.000000
 time_pretransfer=0.145810
 time_starttransfer=0.224296
 time_total=0.224420

我们会发现建立连接所花费的时间大幅降低,再次分析耗时构成

耗时 说明(单位秒) 占比
域名解析耗时 = time_namelookup 0.008063 3.6%
TCP 握手耗时 = time_connect - time_namelookup 0.044245 19.72%
SSL/TSL 耗时 = time_appconnect - time_connect 0.093421 41.63%
服务器处理请求耗时 = time_starttransfer - time_pretransfer 0.078486 35%
TTFB = time_starttransfer - time_appconnect 0.078567 35%
总耗时 = time_total 0.224420

我们可以发现,TCP 握手时间服务器处理请求的时间还有 TTFB 都并没有变化多少,主要是是域名解析和 SSL/TSL 握手花费的时间都大大降低,为什么?

因为域名解析会有缓存,同时 SSL/TSL 握手之后,后面的请求也是会复用已经成功握手的 SSL/TSL 的状态的,并不是每次请求都重新进行 SSL/TSL 握手,一段时间之后,再次发起请求,就会发现连接建立的时间又回到了接近 2s。

简单总结

如果 time_connect(没有 SSL/TSL)或者 time_appconnect 在 time_total 中的占比很大,说明网络状况不好,延迟很大,导致花费在建立连接上的时间很长。

如果 (time_pretransfer – time_appconnect) 在 time_total 中的占比很大,说明建立连接之后,客户端很长时间没有发起请求,说明客户端网络拥挤,不过一般情况下不存在这种情况,time_pretransfer 和 time_appconnect 一般都非常接近。

如果 (time_starttransfer - time_pretransfer) 在 time_total 中的占比很大,说明服务端很繁忙,可能是请求太多处理不过来。

0%