# HTTP/2.0

HTTP/1.1 存在的问题:慢启动和 TCP 连接之间相互竞争带宽是由于 TCP 本⾝的机制导致的,⽽队头阻塞是由于 HTTP/1.1 的机制导致的。

  • 对于慢启动和带宽竞争,HTTP/2 的解决思路是,一个域名只使用一个 TCP 长链接,这样整个页面的资源下载过程只需要一次慢启动,也能避免多个 TCP 链接相互竞争带宽。
  • 对于队头堵塞,等待请求完成后才能去请求下⼀个资源,这种⽅式⽆疑是最慢的,所以 HTTP/2 需要实现资源的并⾏请求,也就是任何时候都可以将请求发送给服务器,⽽并不需要等待其他请求的完成,然后服务器也可以随时返回处理好的请求资源给浏览器。HTTP/2 主要通过多路复用机制来实现这些需求。

# 多路复用

多路复用是指一个页面只使用一个 TCP 长链接,避免多个 TCP 的竞争带宽,且只有一次慢启动;另外也支持多个 HTTP 并行请求,也就是任何时候都可以将请求发送给服务器,⽽并不需要等待其他请求的完成,然后服务器也可以随时返回处理好的请求资源给浏览器。

多路复用的实现是基于二进制分帧层。

# 二进制分帧层

HTTP/2 的请求和接收流程:

  1. 首先,浏览器准备好请求数据,包括请求行、请求头等信息,如果是 POST 方法,就还有请求体。
  2. 这些数据经过二进制分帧层处理后,会被转为一个个带有请求 ID 编号的帧,通过协议栈将这些帧发送给服务器。
  3. 服务器接收到所有帧之后,会将所有相同的帧合并为一条完整的请求信息。
  4. 然后服务器处理该请求,并将处理的响应行、响应头和响应体分别发生至二进制分帧层。
  5. 同样,二进制分帧层会将这些响应数据转换为一个个带有请求 ID 编号的帧,经过协议栈发送给浏览器。
  6. 浏览器接收到响应帧之后,会根据 ID 编号将帧的数据提交给对应的请求。

也就是说,多个 HTTP 请求被分解为多个帧数据,这些帧可以交错(乱序发送),还可以分优先级,最后再在另一端把它们重新组合起来。多路复用意味着来自很多流的数据包能够混合在一起通过同样连接传输。当到达终点时,再根据不同帧首部的流标识符重新连接将不同的数据流进行组装。

# 请求的优先级

把 http 消息分为很多独立帧之后,就可以通过优化这些帧的交错和传输顺序进一步优化性能。HTTP/2 提供了请求优先级,可以在发送请求时,标上该请求的优先级,这样服务器接收到请求之后,会优先处理优先级⾼的请求。

# 服务器推送

服务器可以对一个客户端请求发送多个响应,服务器向客户端推送资源无需客户端明确地请求。并且,服务端推送能把客户端所需要的资源伴随着 index.html 一起发送到客户端(比如一起发送了 CSS,js),省去了客户端重复请求的步骤。

注意两点:
1、推送遵循同源策略;
2、这种服务端的推送是基于客户端的请求响应来确定的。

# 头部压缩

http1.x 的头带有大量信息,而且每次都要重复发送。http/2 使用 encoder 来减少需要传输的 header 大小,通讯双方各自缓存一份头部字段表,既避免了重复 header 的传输,又减小了需要传输的大小。
如果首部发生了变化,则只需将变化的部分加入到 header 帧中,改变的部分会加入到头部字段表中,首部表在 http/2.0 的连接存续期内始终存在,由客户端和服务器共同渐进地更新。

# HTTP/2 的问题

# TCP 的队头堵塞

先回顾一下队头堵塞的概念:HTTP/1.1 通过支持 TCP 长链接,让一个 TCP 长链接可以传输多个 HTTP 请求,省去了 TCP 断开和新建链接的步骤。持久连接虽然能减少 TCP 的建⽴和断开次数,但是它需要等待前⾯的请求返回之后,才能进⾏下⼀次请求。如果 TCP 通道中的某个请求因为某些原因没有及时返回,那么就会阻塞后⾯的所有请求,造成队头阻塞

其实只要使用 TCP 队头堵塞 就会存在,在 HTTP/1.1 中同一域名最多支持 6 个 TCP 长链接,一个发生队头堵塞了,其他 5 个还可以正常运行。但是在 HTTP/2 中只有一个 TCP 长链接,多个请求都在同一个 TCP 中传输,如果其中任意一个数据包出现问题,那么就需要等待该数据包重传,堵塞 TCP 中所有请求。所以随着丢包率增加,HTTP/2 反而传输效率会越来越差。