http 发展
http 1.0
短连接,每次请求都需要客户端与服务器之间建立一次连接, 即三次握手和四次挥手的过程,十分耗费时间
http 1.1
- 长连接,得益于管道技术的使用,建立一次连接,可以持续传输请求和响应,请求头内的 Connection: keep-alive 字段。虽然允许多个请求或响应发送,但须按序处理并按序返回,引发队头阻塞问题。
- 缓存策略,新增强缓存与协商缓存,由响应头内的 cache-control、expires、no-store、no-cache 等字段控制。
- range 头域,允许只请求资源的某一部分,通过 range 字段来设置请求资源的字节范围。
http2.0
- 以帧的形式传输数据,用有效序列标识帧属于哪一个流,每个流代表一个请求或响应。
- 多路复用,允许并发多个请求,允许先完成的请求先响应给客户端,避免了队头阻塞的问题。
- 头部压缩,通讯双方各保存一份头部信息表,第一次请求时将请求头所用的字段保存到表中,后续的请求只发送与之前请求有差异的部分,对于重复的请求字段,只需告知服务器所需的重复字段,服务器直接从头部信息表中读取即可。
各版本资源加载速度对比
来看一个表,假设在同一个域名下,我们有 10 个资源要请求,每个资源的 RTT 都为 100,server 端处理请求的时间假定为 0ms
HTTP1.0
10 个资源,最好的情况下浏览器最多建立 6 个连接,于是有 6 个资源请求被并行发出,100ms 后,拿到这 6 个资源,关闭 tcp 连接,剩下 4 个也重新建立 tcp 连接,100ms 后,拿到这 4 个资源。
总耗时:200ms TCP 连接:10 个
HTTP1.1(连接池复用技术)
10 个资源,同样最好的情况下浏览器最多建立 6 个 tcp 连接,于是有 6 个资源请求被并行发出,100ms 后,拿到这 6 个资源,剩下 4 个资源,从前面建好的 tcp 连接中复用 4 个,100ms 后,拿到这 4 个资源。:
其实 HTTP1.1 也能建立 1 个 tcp 连接,然后并行发出 10 个资源请求,但是 server 端需要按顺序返回,如果 server 在获取一个资源的时候卡住了,那所有的资源都得等这个资源 ready 才 能返回,单点风险很大,因此这个特性没怎么被支持。
http1.1 默认是长连接的,同一个域名,多个资源可共用同一个 tcp 连接,但是同一个 tcp 连接在同一个时间,只能发一个资源请求,如果要并行发请求,还是要新建多个 tcp 连接,只是当前面的 tcp 请求完成后,可在复用这个 tcp 连接发起资源请求
总耗时:200ms TCP 连接:6 个
HTTP2.0
10 个资源,建立 1 个 tcp 连接,然后同时发出 10 个资源请求,server 端在收到请求后,处理完成一个返回一个,在 server 侧,不会因为某个资源卡住而阻塞其他资源的回包(二进制分包),因此 100ms 后,拿到 10 个资源。但是一旦丢包,就会有队头阻塞问题,丢失的包及其后的包都不能被传递给应用层。
总耗时:100ms TCP 连接:1 个
多路复用(本来要建立多个连接来发送请求,现在都复用同一个连接并行发送),同一个域名,新建一个 tcp 连接后,所有请求被并行发出(在应用层,来一个请求,这个请求就被封装成一个 stream header 和 stream body 传递给传输层),服务端收到请求后,分别响应每个请求(获取到相应的资源,就封装成一个 stream header 和 stream body 发给传输层,如此往复)
HTTP3.0
10 个资源,建立一个连接(组合了 TCP 三次握手和 TLS1.3 握手过程),利用 UDP 直接发出 10 个资源请求,server 端在收到请求后,处理完成一个返回一个,在 server 侧不会因为某个资源卡住而阻塞其他资源的回包,因此 100ms 后,拿到 10 个资源。不存在队头阻塞问题,因为 QUIC 是以资源维度划分的,不同的资源分属不同的流,一个资源的包丢失只会影响当前资源。下面有详细例子。
总耗时:100ms TCP 连接:0 个 QUIC 连接:1 个,总体连接耗时要低于 TCP+TLS