聊聊 HTTP/2 的多路复用

大家好,我是前端西瓜哥 。今天我们来聊聊 HTTP/2 的多路复用 。
HTTP/1 下的请求,并不能很好地地利用带宽:一个 TCP 连接同时只能有一个 HTTP 请求和响应 。如果正在发送一个 HTTP 请求,那其他的 HTTP 请求就得排队 。
这种排队会产生一个请求队列,当队头的请求发生意外(比如丢包、服务器响应缓慢),导致比平时要慢得多,就会导致后面的请求被延迟 。这种情况我们称为 队头阻塞(Head-of-line blocking) 。
为了缓解这个问题,浏览器会对同一个域名建立多个 TCP 连接,来实现 HTTP 的并发 。
但这也对服务器造成不小的负担,所以浏览器做了限制,同一个域名下 TCP 连接数最多会在 6 ~ 8 个左右 。
如果网页一次性加载的资源太多,比如大量图片,6 个 TCP 连接数可能也会顶不住 。为了解决一个问题,我们会使用 域名分片(Domain sharding) 的方法,就是将资源放到不同的域名下 。
比如将图片放到专门的 static.xxx.com ,或者 CDN 。因为域名不同,所以总的 TCP 连接数就能突破 6 的限制 。达到 域名数 x 6 。

HTTP/1.1 有一个 pipeline 机制,意图解决不能并发的问题,但因为实现上的缺陷,实质上已经废弃 。浏览器也默认关闭 pipeline 。
为了解决这个问题,HTTP/2 使用了 多路复用 。
HTTP/2 引入了流(stream)和帧(frame)的概念 。
帧是最小的数据单位,HTTP 报文不再是原来的明文的 ASCII 编码,而是会被拆分成一个个的二进制形式的帧 。帧上面除了 HTTP 数据,还包含数据长度、流标识符、帧类型等信息 。
流是一个建立连接后的双向的虚拟字节流,可以承载多个消息 。帧通过自己的流 ID,确定自己属于哪个报文,就可以不按顺序进行请求响应了 。
HTTP/2 会将所有 HTTP 请求打散成帧,在一个 TCP 连接上做并发请求,充分利用 TCP 带宽 。现在浏览器对于 HTTP2,只会建立一个 TCP 连接,减轻了服务端不小压力 。
例子我们举个例子讲解 HTTP/1 升级为 HTTP/2 后利用多路复用带来的优势 。
假设依次请求一个很大的 JS 文件,和一个很小的 css 文件 。
在 HTTP/1 时,TCP 的发送的包是这样的(JS 用多个 1 表示,CSS 用多个 2 表示):
111111111111111111111111222JS 很大,会让 CSS 延迟,我们可能希望比较小的 CSS 能早一点请求完,早一点做解析 。而且 JS 一旦发生了意外发生阻塞,CSS 就更晚才能获取到了 。
现在我们用 HTTP/2,就变成了下面这样:
121212111111111111111111111因为并行的原因,CSS 不仅不用再担心 JS 导致的阻塞,还能更早请求并获取到资源 。
结尾HTTP/2 的多路复用能够解决 HTTP 队头阻塞问题,更充分地利用 TCP 带宽 。
但因为还是在 TCP 上的协议,所以不能解决 TCP 队头阻塞问题,这个问题要交给 HTTP/3 通过 UDP 来解决了,期待一下 。
我是前端西瓜哥,欢迎关注我,学习更多前端知识 。

【聊聊 HTTP/2 的多路复用】


    推荐阅读