我用 curl 对准本站,把一次请求的每个阶段都计了时。总计:1.07 秒。
服务器占其中 278 毫秒。其余——那一秒里的四分之三——全是网络在确认”它到底允不允许跟服务器说话”。
这让我意外,而我就是建这个站的人。我一直把”这个站”理解成 nginx 加一个静态文件,也就是我能控制的那部分。但测量结果说:我能控制的那部分,只是读者等待时间里的少数。
DNS 解析 5.2 msTCP 连接 324.3 msTLS 握手 234.3 ms服务器处理 278 ms传输 229.5 ms
1DNS 解析5.2 ms
$ getent hosts blog.awin-x.top 88.218.79.95 blog.awin-x.top如果这一步很慢,问题不在服务器,在你的解析器。
2TCP 连接324.3 ms
* Host blog.awin-x.top:443 was resolved. * IPv4: 88.218.79.95 * Trying 88.218.79.95:443...注意这里没有 IPv6 记录——只有一条 A 记录。
3TLS 握手234.3 ms
* TLSv1.3 (OUT), TLS handshake, Client hello (1): * TLSv1.3 (IN), TLS handshake, Server hello (2): * TLSv1.3 (IN), TLS handshake, Certificate (11): * TLSv1.3 (IN), TLS handshake, Finished (20): * SSL connection using TLSv1.3 / TLS_AES_256_GCM_SHA384 / x25519 / RSASSA-PSS * ALPN: server accepted h2一次往返就完成了——TLS 1.3 的握手只需要 1-RTT,这是它比 1.2 快的地方。
4请求
* using HTTP/2 * [HTTP/2] [1] OPENED stream for https://blog.awin-x.top/ > GET / HTTP/2 > Host: blog.awin-x.topHTTP/2 里,请求是流(stream),一条连接上可以同时开很多条。
5响应278.0 ms → TTFB
< HTTP/2 200 < server: nginx/1.18.0 (Ubuntu) < content-type: text/html < content-length: 15122 < strict-transport-security: max-age=31536000 < x-content-type-options: nosniff服务端只花了不到 300 毫秒——对一台 981 MB 的小机器上的静态文件来说,这是正常水平。
6传输229.5 ms
< content-length: 15122 * Connection #0 to host blog.awin-x.top left intact总共 1.07 秒。其中真正"服务器"的部分只占四分之一,其余全在网络往返上。
服务器只占四分之一,而我一直在优化错的地方
再看一眼那根时间条:DNS 是个舍入误差(5 毫秒,因为答案在缓存里),真正重的是 TCP 连接、TLS、以及传输本身。文件只有 15 KB,那里已经没有东西可挤了。
而我过去当成”性能工作”来做的那些事——更小的 HTML、更少的请求、更精简的构建——动的是那 278 毫秒的阶段。就算我让 nginx 瞬间响应,这次请求仍然要花约 800 毫秒,因为一个欧洲读者,离一台只有一条 A 记录、一个 IP 的机器就是 300 毫秒。
往返就是税,而 TLS 1.3 已经退了一笔
握手这一段最值得理解,因为它最近刚变过:
* TLSv1.3 (OUT), TLS handshake, Client hello (1):
* TLSv1.3 (IN), TLS handshake, Server hello (2):
* SSL connection using TLSv1.3 / TLS_AES_256_GCM_SHA384 / x25519 / RSASSA-PSS
* ALPN: server accepted h2
这是一次往返。在 TLS 1.2 下,这个交换需要两次,HTTP 的第一个字节才能动——服务器必须先发出证书,等客户端确认,才肯接受请求。TLS 1.3 把这一步折叠掉了;在 150 毫秒延迟的链路上,每条连接大约省下 150 毫秒。
这就是”即使看起来像无谓工作,也要把 TLS 保持在新版本”的理由。它不只是安全升级;在长链路上它是一次延迟升级,而且会出现在同一份测量里。
如果真要改,我会按这个顺序
如果这个站很慢,我会按下面的顺序动手——而这跟我原先以为的顺序不一样:
- 复用连接。 那份 trace 里最大的数字,是”连接 + TLS”这一对。读者看一页,付一次;看五页,也应该只付一次。HTTP/2(trace 里 ALPN 协商出来的那个)让这件事成为可能,把 CDN 放到离读者更近的地方也是。
- 机器放在哪。 324 毫秒去连接,是地理问题,不是工程问题。换机房、或者在前面放一层缓存,改变这个数字的方式,是任何 nginx 调优都做不到的。
- 服务器。 然后,也只有到这时,才是那 278 毫秒——而在一个静态站上,它基本上就是在从磁盘读一个文件。
这条测量只需要一条命令,而且值得先对你自己的站跑一遍,再去相信任何关于”时间花在哪”的说法:
curl --noproxy '*' -o /dev/null -sS \
-w 'dns=%{time_namelookup}s connect=%{time_connect}s tls=%{time_appconnect}s ttfb=%{time_starttransfer}s total=%{time_total}s\n' \
https://blog.awin-x.top/
我在这里修不了的那部分
诚实的结论有点难受:对一个离这台小机器很远的读者来说,下限由距离决定,服务器里做多少工作都挪不动它。我能做的,是不再为不需要的往返付费——把 TLS 保持在新版本、把连接保持活着,将来再把某个离读者更近的东西放到前面。
真正会改变体验的那个数字,根本不在服务器上。它是服务器听到请求之前的那 324 毫秒。