📚 第一章 网络基础知识
📄 一句话概括

打开一个网页,前两段(解析、建连)是纯开销,一个字节内容都还没发。 这两段与网页大小无关,所以「首屏慢」往往是它们造成的。

📝 本节和前面几节的关系

1.10.1 讲的是「经过谁」,这一节讲「时间花在哪」。 这一节的三段拆解,就是前端性能优化的基本原理,剩下都是细节。

一、三段固定开销

把一次网页请求拆成三段计时:

阶段 做了什么 实测 ms
域名解析 名字 → 一个 IP 19.6
TCP 建连 三次握手接通 168.5
合计 还没开始传数据 188.2

第三行那个「还没开始传数据」是最该记住的一句:这两步是纯开销, 它们的时间只取决于「网络有多远」,与网页有多大完全无关。 所以一个很小的页面也可能打开很慢——慢在开头,不在传输。 注意这两段的量级差:解析 19.6 毫秒、建连 168.5 毫秒, 建连是大头,因为它要一个完整的往返。 而解析为什么快?因为 DNS 结果被本机缓存过——第一次解析通常不会这么便宜。

二、三段各有各的优化

每一段的对策完全不同,不能混着用:

阶段 怎么优化 原理
域名解析 DNS 预取、本地缓存 把 19.6 变成接近 0
TCP 建连 长连接、连接池 省掉重复的握手
请求响应 压缩、CDN、合并请求 减少字节与往返

第二行那个「长连接」收益最大:一次握手 168.5 毫秒,一个页面要请求几十个资源, 不保持连接就要握手几十次——这就是 HTTP/1.1 默认 keep-alive 的原因。 也是「连接池」这个词的由来:连接是可以复用的资源,不是一次性的。 第一行那个「DNS 预取」很便宜:在 HTML 里加一行 rel=dns-prefetch,浏览器就会提前解析。 它不保证有用(用户可能不点那个链接),但成本几乎为零。 第三行那个「合并请求」是 HTTP/1.1 时代的特产: 因为 1.1 有「同域并发连接数」限制(通常 6 个),所以要把小文件合并。 HTTP/2 多路复用之后,这个优化反而变成反模式——合并会让缓存粒度变粗。

三、为什么首屏慢

把三段叠起来看,就明白「首屏」卡在哪:

环节 能不能省 省不掉的原因
域名解析 能接近 0 缓存、预取
TCP 建连 能省一次 长连接复用
TLS 握手 要多一个往返 加密必需
首字节时间 省不掉 服务端要算

第三行是 HTTPS 比 HTTP 多出来的那一段:TLS 握手要多一到两个往返, 所以在高延迟链路上,HTTPS 的首屏会明显更慢。 这不是「加密很慢」——加密的计算开销很小,慢的是「多出来的往返」。 TLS 1.3 把握手压到一个往返,就是在解决这个问题。 第四行那个「服务端要算」是唯一真正省不掉的: 数据库查询、模板渲染,再快也要几毫秒到几百毫秒。 所以「首屏优化」的极限就是「服务端算完第一字节的时间」,网络再怎么优化也突破不了它。

四、动手 Demo:一次完整的互联网通信,时间花在哪

把打开一个网页拆成三段计时:解析、建连、请求响应。 重点是前两段的合计——那是不含任何内容的纯开销。

import socket, time

host = 'www.python.org'
t0 = time.perf_counter()
ip = socket.gethostbyname(host)
dns = (time.perf_counter() - t0) * 1000

t0 = time.perf_counter()
c = socket.create_connection((host, 443), timeout=10)
tcp = (time.perf_counter() - t0) * 1000
c.close()

print('%-10s %-24s %.1f ms' % ('域名解析', ip, dns))
print('%-10s %-24s %.1f ms' % ('TCP 建连', '接通', tcp))
print('%-10s %-24s %.1f ms' % ('合计', '还没开始传数据', dns + tcp))

实跑输出:

--------------------------------------------------------------------
一次完整的互联网通信,时间花在哪
--------------------------------------------------------------------
**把「打开一个网页」拆成三段计时**:**域名解析、TCP 建连、请求响应。**

  阶段         做了什么                  耗时ms
  ----------------------------------------------
  域名解析     名字 → 151.101.64.223     19.6
  TCP 建连     接通                      168.5
  合计         **还没开始传数据**        188.2

**注意最后一行那个「还没开始传数据」**:**这两步全是纯开销**,一个字节的网页内容都还没发。
**这就是「首屏慢」的典型原因**:**网络越远,这两段固定开销越大,而且与网页大小无关。**

**三段各有各的优化手段**:

  阶段         怎么优化
  -------------------------------------------------------
  域名解析     **DNS 预取、本地缓存、就近的 DNS 服务器**
  TCP 建连     **长连接、连接池、就近接入**
  请求响应     **压缩、CDN、减少请求数**

**这三条加起来就是前端性能优化的全部基本原理**——**剩下的都是细节。**

解析 19.6 毫秒、建连 168.5 毫秒、合计 188.2 毫秒:建连是大头,因为它要一个完整往返。 而解析这么快,是因为本机缓存过这个域名——第一次解析通常要几十到几百毫秒。 这 188 毫秒里一个字节的网页内容都没发: 这就是「首屏慢」的典型原因——网络越远,这两段固定开销越大,与网页大小无关。 注意这里连的是 443 端口但没有做 TLS 握手:所以这 168.5 毫秒只是 TCP 的往返。 真实的 HTTPS 还要再加一到两个往返——这就是「为什么加了 HTTPS 之后首屏变慢」的直接原因。

30 秒实验

  1. 把 host 换成一个从没访问过的域名,看解析时间变多少。
  2. 连跑三次,看建连时间是否稳定——第一次往往更慢(要建路由缓存)。
  3. 用开发者工具的「网络」面板看一个真实页面,数一下「排队/等待」占了多少时间。

常见误区

  1. 以为网页小就一定快。 前两段是纯开销,与网页大小无关,小页面也可能因为解析与建连而慢。
  2. 以为 HTTPS 慢是因为「加密计算」。 加密很快,慢的是 TLS 握手多出来的一到两个往返。
  3. 以为优化就是「压缩图片」。 压缩只作用于第三段,前两段要靠缓存与长连接。
自测三问(点开对答案) 1. 打开网页的前两段固定开销是什么? —— **域名解析与 TCP 建连**,合计约 188 毫秒,这段时间里一个字节的网页内容都还没发。 2. 为什么长连接的收益最大? —— **一次握手约 168 毫秒,一个页面要请求几十个资源**,不保持连接就要握手几十次。 连接是可复用的资源。 3. HTTPS 比 HTTP 慢在哪? —— **慢在 TLS 握手多出来的一到两个往返**,不是加密计算。 所以 TLS 1.3 把握手压到一个往返就是在解决这个问题。

下一节讲移动通信——为什么它最难的不是速率,是「切换」。

相关笔记