在策略层面,(四维空间针对指定问题绝对复杂性的)普适优化手段最终都是在两种维度之间进行取舍转换:

  • 当时间更宝贵时,用空间换时间;
  • 当空间更宝贵时,用时间换空间。

以 Web 应用为代表的互联网肇始于 1991 年 Tim Berners-Lee 提出的 HTTP/0.9 协议,经过 30 多年的发展,伴随着 IT 巨头的明争暗斗,已经逐渐渗透进我们生活的方方面面,成为当代社会不可或缺的组成部分。

HTTP 协议的历史

1991 年,HTTP(HyperText Transfer Protocol,超文本传输协议)第一个版本 — HTTP/0.9,诞生了,功能比较简陋,设计目标包括:

  • 支持文件传输;
  • 能够通过索引搜索 HTML;
  • 格式化协商机制;
  • 能够把客户端引导到不同的服务器。

HTTP/0.9 奠定了整个互联网交互的大框架:

  • 客户端(请求)—服务器(响应)模式
  • 基于 TCP/IP 协议栈

在 HTTP/0.9 版本中,TCP 连接在每次 HTTP 请求后都会关闭。

1996 年,HTTP/1.0 版本(rfc1945)发布,主要是参考性质的,总结了一些最佳实践和模式,增加了内容协商,响应对象不再局限于 HTML,彼时一个请求仍然对应一个 TCP 连接。

1997 年,HTTP/1.1 版本(rfc2068)正式发布,厘清了之前版本中很多有歧义的地方,并对性能进行了优化:

  • 持久连接,复用 TCP 连接;
  • 分块编码传输;
  • 字节范围请求;
  • 增强的缓存机制;
  • 传输编码;
  • 请求管道,不用等待前一个请求回应就发送下一个请求,支持的不多。

2015 年,HTTP/2 版本(rfc7540)发布,主要聚焦性能,将原来基于文本传输的模式改为基于二进制数据,在传输层之上引入二进制分帧数据层,每个【主机:端口】组合仅一个持久的 TCP 连接,在该连接上进行多路复用。

2022 年,HTTP/3 版本(rfc9114)发布,使用的传输层协议从 TCP 改为 UDP。

从 HTTP/0.9 到 HTTP/3,针对不同的场景和需求,协议在不断地进化,但现实世界中难以对大量的软硬件进行统一升级,所以无法做到一键切换,新旧协议的交替往往需要持续一段相当长的时间(一般 5-10 年),所以在一段时间里,客户端、代理、服务器仍然需要同时支持多种版本的 HTTP 协议。

性能优化

对 Web 应用来说,性能优化可以简要描述为:如何更快地完成用户请求

个人理解的核心原则是:(在大多数时候)尽量减少网络请求。—— 有点像车轱辘话 🤔。

具体手段有:

1、使用压缩

通过在请求中声明标头【Accept-Encoding: <压缩算法>】来和服务端协商对 body 进行压缩后传输,压缩算法有 gzip、deflate、compress、br,一般选用 gzip。如果服务端同意,后续响应中就对 body 进行压缩,并且响应中包含标头【Content-Encoding: <压缩算法>】。

使用压缩可以减少需要传输的数据量,从而减少了需要传输的网络包,增加了两端的处理时间,减少了中间的传输时间,属于以时间(两端的 CPU 时间)换空间(传输数据量)的优化策略。

2、CDN

将资源服务器托管到靠近用户的网络运营商处,用户请求时直接请求 CDN,减少了网络跳数,保证网络延迟最小。

3、条件式请求

当资源过期后,通过传递资源的版本标识,询问服务端该缓存资源是否可用。如果可用的话,服务端返回【304 Not Modified】,就成功避免了一次多余的数据响应,浏览器会根据场景调整缓存的有效性。

资源有两种标识方式:

  • Last-Modified: <date/time>,最后修改时间;
  • Etag: <string>,版本号。

服务端返回资源时携带资源标识。

当请求中包含以下标头时,就是条件式请求:

  • If-Match: <etag_value>,版本号匹配时才执行;
  • If-None-Match: <etag_value>,版本号不匹配时才执行;
  • If-Modified-Since: <day-name>, <dd> <mm> <yyyy> <hh>:<mm>:<ss> GMT,在此日期之后修改过才执行;
  • If-Unmodified-Since: <day-name>, <dd> <mm> <yyyy> <hh>:<mm>:<ss> GMT,在此日期之前修改过才执行;
  • If-Range: <day-name>, <dd> <mm> <yyyy> <hh>:<mm>:<ss> GMT,用于断点续传;
  • If-Range: <etag_value>,用于断点续传。

和 PUT 方法一起用时,就是乐观锁的思路。

4、Expires

当服务端第一次返回资源时,设置标头【Expires: <有效时间>】,该资源缓存在客户端,在有效时间内,资源都是 fresh 状态的,可以直接取用,当过期以后,资源就变为 stale 状态,需要重新和服务端协商获取。

5、Cache-Control

HTTP 协议中定义了两类缓存:

  • 私有缓存,特定用户所有,通常放在浏览器端;
  • 共享缓存,可以在多个用户之间共享,可以放在路径上的代理、CDN、反向代理、浏览器端。

可以通过标头 Cache-Control 来控制缓存策略。

Cache-Control 的指令可以组合适用(通过逗号分隔),可选指令包含:

  • public,声明为共享缓存;
  • private,声明为私有缓存,只能被用户浏览器缓存;
  • no-cache,在用缓存进行响应前,必须把请求提交给服务器验证有效性;
  • no-store,禁用缓存;
  • max-age=<秒>,缓存到期时间;
  • s-maxage=<秒>,覆盖 max-aqge 或 Expires 头,仅适用共享缓存;
  • must-revalidate,资源过期后必须向服务器验证,否则不能作为响应;
  • proxy-revalidate,共享缓存中的资源过期后必须向服务器验证,否则不能提供;
  • no-transform,不得对资源进行转换或转变,如代理不能修改 Content-Encoding、Content-Range、Content-Type 标头。
浏览器端的缓存层次

6、持久连接

在 HTTP/1.1 之前,一个 HTTP 请求会申请一个 TCP 连接,请求结束即关闭连接,下一个请求需要重新申请 TCP 连接。TCP 连接的建立是通过三次握手,也就是每个请求至少需要延迟一个网络往返时间才能得到响应,而这部分延迟是可以避免的。

于是在 HTTP/1.1 中引入了持久连接,一个请求结束后并不会立即关闭该 TCP 连接,允许其他 HTTP 请求继续通过它发送数据。在 HTTP/1.1 中,默认情况下 TCP 连接是持久的,只是有些程序库(出于简化编程模型的目的)在实现时会简单新发起一个 TCP 连接。持久连接可以通过设置标头【Connection: close】来关闭。所谓的持久连接,就是指保持传输层的 TCP 连接持久。日常一般简称长连接。

7、避免重定向

重定向通常会引起一次 DNS 解析和一次 TCP 三次握手,增加了一定的延迟。

8、合并静态文件

由于目前的网页越做越大,一个网页通常包含很多静态文件(JS、CSS、Image、…),资源越多,请求数量越多(资源路径不同、HTTP/1.0 不支持持久连接),可以将多个静态资源合并为少量的几个,这样 HTTP 请求也相应地减少了。

会引入一些问题,如当某个静态文件发生变化时,需要重传整个大的静态文件;当某个网页只需要部分静态资源时,却必须请求整个静态文件,多了很多不需要的数据。

9、域名分区

在 HTTP/1.x 协议中,要求浏览器针对每个【域名+端口】维护一个连接池,每个连接池最多包含 6 个 TCP 连接,如果不够用,服务端可以设计不同的【域名+端口】来分配资源,以突破浏览器端 6 个 TCP 连接的限制,提高传输效率。

10、多个 TCP 连接

在 HTTP/1.x 中不支持多路复用,可以创建多个 TCP 连接,并行请求服务端资源。多个 TCP 连接也可以避免单个 TCP 持久连接中的队首阻塞问题(因为 tcp 保障按序传输,发送序列中的某一个分组传输失败,后续分组到达接收端后只能保存在 tcp 缓冲区中,等待失败的分组重发并到达后才能交付给应用程序)。

11、升级到 HTTP/2

http/2 引入多路复用、头部压缩功能和流优先级等新特性。

多路复用是将不同的 http 请求当作不同的流,每个流由若干帧组成,不同流的帧可以混杂放到一个持久连接中,从而达到多个请求共用一个 TCP 持久连接的效果。

HTTP/2 引入了头部压缩功能,在客户端和服务端保持一个头部表来跟踪头部的键值对。能减少需要传输的数据量(如 Cookie、User-Agent)。

在 HTTP/2 中,一个【域名+端口】只允许建立一个 TCP 连接,且这个连接是持久的。

一般 HTTP/2 会直接搭配 TLS 使用,如在 nginx 中就可以在 ssl 后配 http2。

这种模式没有解决队头阻塞问题,所以也为 HTTP/3 留下了想象的空间。

12、Server Push

HTTP/2 引入的新特性。发送一个请求后,服务端推送多个响应。但服务端不能任意推送响应,必须是在第一个(initial)请求中包含的特定链接所指向的内容。Push 是服务端到客户端的一个单向通道。


参考:

  • 《Web 性能权威指南》
  • 《HTTP/2 in Action》
  • MDN

2 条评论

опрессовка · 2023年1月9日 下午5:31

You are so awesome! I don’t believe I’ve read a single thing like that before.
So nice to discover somebody with a few unique thoughts on this subject matter.

Really.. many thanks for starting this up. This website is one thing that’s needed on the
web, someone with a bit of originality!

    顽石 · 2023年1月10日 上午8:20

    Thanks. There are many people sharing their original ideas and interests on the web which makes the world more interesting.

发表回复

您的电子邮箱地址不会被公开。 必填项已用 * 标注