📘 第一章 了解Web及网络基础
📄 一句话概括

网页从几 KB 的纯文本长到几 MB 的应用,靠的不是带宽变大这一件事,而是同一份内容传得更少: 压缩、缓存、内联、按需加载。

📝 本节和前面几节的关系

上一节讲 Web 的出身,这一节讲它的体重。 后面第三章的「内容编码」、第六章的 Accept-Encoding、第九章的 SPDY/HTTP2, 都是在给这一节的重量找办法。

一、两条路:把管道加粗,或者把东西变轻

2000 年前后一个页面几十 KB,现在动辄两三 MB。带宽涨了上百倍,页面重了上百倍——刚好抵消。 所以「网速快了页面却没快多少」不是错觉。 真正让页面能跑起来的是第二条路: - 压缩:文本类内容 gzip 后常剩一成到三成; - 缓存:第二次访问一个字节都不传(第六章 ETag/Cache-Control); - 内联:小图标塞进 CSS 或 HTML,省掉一次请求的往返; - 按需加载:首屏只给首屏要的(这条书里没有,是后来的事)。

二、实测:压缩和内联各赚多少

Demo 里造了 2376 字节的 HTML,gzip 之后剩 224 字节,只剩 9.4%,省掉九成。 这是文本重复度高的结果:标签大量重复,字典一压就没了。 同样的内容改用 base64 内联,会胀到 3168 字节,是原来的 1.33 倍。 原因很简单:base64 把 3 个字节编成 4 个字符,天然 4/3。 所以「内联省请求」和「内联变大」是一笔买卖:只有当省下的往返时间大于多传的字节时间,才值得。 这也是为什么内联只给很小的资源用。

三、压缩不是万能的

gzip 对已经压过的格式(JPEG、PNG、MP4、woff2)几乎无效,再压一遍可能更大,还白烧 CPU。 判断依据就是 Content-Type。 另外压缩有个安全副作用:压缩率会泄露内容。 攻击者往请求里塞猜测的字符串,看响应长度变化就能猜出明文,这叫 BREACH/CRIME。 所以带敏感信息的响应常常关闭压缩,或者把密钥和用户输入分开压缩。

四、动手 Demo:同一份 HTML:gzip 与 base64 的体积账

造一段六张卡片的 HTML,重复四遍,分别算原始、gzip、base64 三种体积。 全是标准库,gzip 与 base64 各一行。

raw = txt.encode()
gz = gzip.compress(raw, 9)
b64 = base64.b64encode(raw)
print(u'原始', len(raw))
print(u'gzip 之后 %.1f%%' % (len(gz) * 100.0 / len(raw)))
print(u'base64 后 %.2f 倍' % (len(b64) * 1.0 / len(raw)))

实跑输出:

--------------------------------------------------------------------
网页是怎么长大的:压缩与内联各赚多少
--------------------------------------------------------------------
  原始 HTML                  2376 字节
  gzip 之后                  224 字节(9.4%)
  省下                       2152 字节
  改 base64 内联             3168 字节(原体积的 1.33 倍)
  同一份内容,加一层压缩省下 91%,塞进 base64 反而胀到 1.33 倍。
  Web 这些年能装下越来越多东西,一半靠带宽,一半靠「同一份内容传得更少」。

gzip 之后 224 字节(9.4%)——这就是 Content-Encoding: gzip 的全部收益。 注意这是重复度很高的样本;真实页面一般压到 20%~30%,所以「省九成」不能当通用结论。 改 base64 内联 3168 字节(原体积的 1.33 倍)——4/3 这个系数来自编码本身,跟内容无关, 永远是这个数。

30 秒实验

  1. 把六张卡片换成六段完全不同的文字,再跑一次,看 gzip 还能不能压到一成。
  2. 先 gzip 再 base64,算它的体积,和「只 base64」比一比,想想为什么线上从不这么传。
  3. 拿一张 JPEG 用 gzip 压一遍,看体积是变大还是变小。

常见误区

  1. 「压缩率越高越好」——压缩要烧 CPU,而且高度压缩的响应更容易被 BREACH 类攻击利用。
  2. 「base64 是压缩」——它是编码,体积一律涨三分之一,目的是让二进制能塞进文本协议。
  3. 「带宽涨了页面就会快」——页面重量同步在涨,抵消掉了大部分收益。
自测三问(点开对答案) 1. 为什么 base64 一定膨胀 4/3? —— 它把每 3 个字节映射成 4 个可打印字符。不够 3 字节的尾巴用 `=` 补齐,只会更大。 2. gzip 对 JPEG 为什么无效? —— JPEG 内部的熵已经接近极限,没有冗余可压,再压只会增加几十字节的头。 3. 内联小图标的取舍点在哪? —— 省下的一个往返时间 vs 多传的三分之一字节。资源越小,越值得内联。

内容变轻了,可协议没怎么变。下一节看 HTTP/1.1 到底卡在哪。

相关笔记