HTTP/2(简称 HTTP2)于 2015 年由 IETF(Internet Engineering Task Force)发布。
-
RTT: Round Trip Time, 往返时间, 从发送端发送数据开始,到发送端收到来自接收端的确认(接收端收到数据后便立即发送确认),总共经历的时间就是一个 RTT。
-
带宽:单位时间内,网络中的数据传输量。
-
TCP 长连接:TCP 长连接是指在一个 TCP 连接上可以连续发送多个数据包,在 TCP 连接保持期间,如果没有数据包发送,需要双方发检测包以维持此连接,一般操作系统的 TCP/IP 协议栈都会实现此功能,保持连接的时间称为超时时间。
TCP 慢启动是为了防止新的连接过快地将大量数据注入到网络中,从而引起网络拥塞。其设计的目的是在连接刚建立时,逐步测试网络的拥塞情况,以便在达到网络容量上限之前逐渐提高发送速率。这样可以确保新连接不会立即引发拥塞,给网络一个缓冲的时间以适应新的数据流。
具体来说,TCP 慢启动的实现方式是在刚开始的时候,将拥塞窗口(Congestion Window)设定为一个较小的值,然后随着时间的推移逐渐增加。拥塞窗口的大小决定了可以发送的数据量。在每个往返时间(Round-Trip Time,RTT)之后,拥塞窗口的大小会根据网络的情况进行调整。这样,TCP 连接会在网络上逐步增加其传输速率,从而避免过快地注入大量数据导致网络拥塞。
举个例子,假设有一个新的 TCP 连接,而且网络管理员设定了一个初始的拥塞窗口大小为 2 段(每段代表一定数量的数据,比如字节)
- 初始阶段:
- 发送方发送两个数据段到接收方。
- 接收方确认收到这两个数据段,并且发送一个确认(ACK)回给发送方。
- 此时,拥塞窗口的大小会翻倍,变成 4。
- 下一个往返时间(RTT):
- 发送方现在可以发送 4 个数据段。
- 接收方再次确认收到这 4 个数据段,并发送 ACK。
- 拥塞窗口翻倍,变成 8。
- 继续往返时间:
- 发送方可以发送 8 个数据段。
- 接收方确认并发送 ACK。
- 拥塞窗口变成 16。
这个过程会一直继续下去,每经过一个往返时间,拥塞窗口的大小就翻倍,直到达到某个限制(例如,网络容量上限或拥塞发生)。当发生拥塞时,TCP 会进入拥塞避免阶段,采用线性增加的方式来调整拥塞窗口的大小。
想象一下,你是一个建筑工地上的水管工程师,负责设计水管系统,将水从水源运送到建筑物内的水龙头。每一根水管都代表一个 TCP 连接。
- 你刚刚安装了一根全新的水管,这个时候你并不确定这根水管的质量和承载能力,为了避免把太多的水一下子灌入管道导致漏水或爆管,你决定初始时只允许少量的水进入,然后逐步增加。通过逐步增加水流,测试管道的质量和容量。
- 假设在工地上有一个需要大量用水的任务,比如混凝土搅拌机需要大量水来完成工作。由于你采用了慢启动策略,一开始水流较小,可能会导致混凝土搅拌机在初始阶段无法获得足够的水,工作效率较低,(⻚面中常用的一些关键资源文件本来就不大,如 HTML 文件、CSS 文件和 JavaScript 文件,通常这些文件在 TCP 连接建立好之后就要发起请求的,但这个过程是慢启动, 所以耗费的时间比正常的时间要多很多,这样就推迟了宝贵的首次渲染⻚面的时⻓了)
在 HTTP/1.1 中,为了提高页面资源的加载速度,浏览器通常采用并行下载的策略,即通过同时使用多个 TCP 连接来获取页面中的各种资源(如 CSS、JavaScript、图像等)。每个 TCP 连接都会经历自己的 TCP 慢启动阶段,初始时发送速率较慢,逐步增加。这是因为每个连接都需要在建立时通过慢启动机制来测试网络的拥塞状况。
当多个 TCP 连接同时尝试占用网络带宽时,它们可能会在各自的慢启动阶段竞争可用的带宽资源。当发现带宽不足的时候,各个 TCP 连接就需要动态减慢接收数据的速度。但是多个 TCP 之间并没有优先级的概念, 导致在整个页面加载过程中,某些关键资源的加载被拖慢,因为它们所使用的连接未能迅速达到其理想的传输速率.
HTTP/1.1 使用了持久连接(Persistent Connection)来复用 TCP 连接,但是每个 TCP 连接在同一时间只能处理一个请求。
当一个资源在一个 TCP 连接中被阻塞时,该连接上的后续请求也会受到影响,因为所有请求必须按照发送的顺序进行处理。如果前面的请求因为某些原因而被阻塞,那么后续请求也将被延迟。(想象排队办业务的场景,假设你所在队伍前面的人办理业务的时间很长,那么你就需要等待很长时间。)
前面我们分析了 HTTP/1.1 的一些问题,我们来看看 HTTP/2 是如何解决这些问题的
- 慢启动和 TCP 连接之间相互竞争带宽是由于 TCP 本身的机制导致的
我们没有替换掉 TCP 的能力,只能曲线救国,通过减少 TCP 连接的数量来减少 TCP 慢启动的影响, 因此,HTTP/2 中针对一个域名建立一个 tcp 长连接
- 队头阻塞是由于 HTTP/1.1 的机制导致的(每个域名 6 个 TCP 连接)
HTTP/2 中,一个 TCP 连接上可以并行处理多个请求,每个请求都有一个唯一的 ID,这样不用等到前一个资源加载完成
-
HTTP/2 通信的最小单位是帧。一个帧是一个二进制块,包含了特定类型的信息,比如请求、响应、头部数据等。每个帧都有一个唯一的标识符,称为流(Stream)标识符,用于区分不同的请求或响应。
-
一个流是一个虚拟的通道,用于在一个连接上承载双向的消息。每个流都有一个唯一的流标识符。请求和响应被分割成一个或多个帧,并通过流来传输。
-
在一个连接上可以同时存在多个流,每个流独立传输。这样多个请求和响应可以并发地在同一连接上进行,避免了在 HTTP/1.1 中每个请求需要一个独立连接的情况。 不同流之间的帧可以交错传输,允许并发处理多个流(类似于操作系统中任务的交错执行,每个任务在不同的时间片段内交替执行),提高了整体性能
机制 | 用途 | 示例 | 描述 |
---|---|---|---|
HTTP/2 优先级 | 服务器推送、流控制 | Priority: {stream_id} {weight} {exclusive} |
使用权重和对另一个流的依赖来指定流的优先级。有助于管理并发流。 |
Link Importance | 资源加载 | <link rel="stylesheet" href="style.css" importance="high"> |
在 HTML 头部中指定资源的重要性。可以是"high"、"low"或"auto"。 |
Prefetch | 浏览器资源提示 | <link rel="prefetch" href="example.css"> |
在页面加载后异步预获取指定的资源(比如在另外的组件中使用的非首屏资源)。 |
Preload | 显式资源提示 | <link rel="preload" href="example.css" as="style"> |
显式提示浏览器预加载和获取资源。其中 as 指定资源的类型 |
打开开发者工具,选择 Network 选项卡,右键 status, 勾选 Protocal, 二次刷新页面,就可以看到了