HTTP1.1、HTTP2.0的区别

本文最后更新于:2023年3月19日 晚上

本文转自:https://coffe1891.gitbook.io/frontend-hard-mode-interview/1/1.5.3

Http 几个版本的区别

HTTP/2: the Future of the Internet》 是 Akamai 公司建立的一个官方的演示,用以说明 HTTP/2 相比于之前的 HTTP/1.1 在性能上的大幅度提升。 同时请求 379 张图片,从 Load time 的对比可以看出 HTTP/2 在速度上的优势。

HTTP2.0 和 HTTP1.X 相比的新特性

二进制分帧层

HTTP2.0 性能增强的核心,全在于新增的二进制分帧层,它定义了如何封装 HTTP 消息并在客户端与服务器之间传输。这里所谓的“层”,指的是位于套接字接口与应用可见高层 HTTP API 之间的一个新机制:HTTP 语义,包括各种动词、方法、首部,都不受影响,不同的是传输期间对它们的编码方式变了。HTTP1.x 以换行符作为纯文本的分隔符,而 HTTP2.0 将所有传输的信息分割为更小的消息和帧,并对它们采用二进制格式的编码。
这样一来,客户端和服务器为了相互理解,必须都使用新的二进制编码机制:HTTP1.x 客户端无法理解只支持 HTTP2.0 的服务器,反之亦然。不过不要紧,现有的应用不必担心这些变化,因为客户端和服务器会替它们完成必要的分帧工作。
HTTPS 是二进制分帧的另一个典型示例:所有 HTTP 消息都以透明的方式为我们编码和解码,从而实现客户端与服务器安全通信,但不必对应用进行任何修改。HTTP2.0 的工作原理差不多也是这样。

流、消息和帧

新的二进制分帧机制改变了客户端与服务器之间交互数据的方式。为了说明这个过程,我们需要了解 HTTP2.0 的几个新概念:

已建立的连接上的双向字节流。 消息 与逻辑消息对应的完整的一系列数据帧。 HTTP2.0 通信的最小单位,每个帧包含帧首部,至少也会标识出当前帧所属的流。

HTTP2.0 通信都在一个连接上完成,这个连接可以承载任意数据量的双向数据流。相应地,每个数据流以消息的形式发送,而消息由一或多个帧组成,这些帧可以乱序发送,然后再根据每个帧首部的流标识符重新组装。HTTP2.0 的所有帧都采用二进制编码,所有首部数据都会被压缩。
这简简单单的几句话里浓缩了大量的信息:

  • 所有通信都在一个 TCP 连接上完成;

  • 流是连接中的一个虚拟信道,可以承载双向的消息。每个流都有一个唯一的整数标识符;

  • 消息是指逻辑上的 HTTP 消息,比如请求、相应等,由一或多个帧组成;

  • 帧是最小的通信单位,承载这特定类型的数据,如 HTTP 首部、负荷等;

简言之,HTTP2.0 把 HTTP 协议通信的基本单位缩小为一个一个的帧,这些帧对应着逻辑流中的消息。相应地,很多流可以并行的在同一个 TCP 连接上交换消息。

多路复用(MultiPlexing)

在 HTTP1.x 中,如果客户端想发送多个并行的请求以及改进性能,那么必须使用多个 TCP 连接。这是 HTTP1.x 交付模型的直接结果,该模型会保证每个连接每次只交付一个响应(多个响应必须排队)。更糟糕的是,这种模型也会导致队首阻塞,从而造成底层 TCP 连接的效率低下。
HTTP2.0 中新的二进制分帧层突破了这些限制,实现了多向请求和响应:客户端和服务器可以把 HTTP 消息分解为互不依赖的帧,然后乱序发送,最后再在另一端把它们重新组合起来。
把 HTTP 消息分解为独立的帧,交错发送,然后在另一端重新组装是 HTTP2.0 最重要的一项增强。事实上,这个机制会在整个 Web 技术栈中引发一系列连锁反应,从而带来巨大的性能提升,因为:

  • 可以并行交错的发送请求,请求之间互不影响;

  • 可以并行交错的发送响应,响应之间互不干扰;

  • 只使用一个连接即可并行发送多个请求和响应;

  • 消除不必要的延迟,从而减少页面加载的时间;

  • 不必再为绕过 HTTP1.x 限制而多做很多工作。

总之,HTTP2.0 的二进制分帧机制解决了 HTTP1.x 中存在的队首阻塞问题,也消除了并行处理和发送请求及响应时对多个连接的依赖。结果就是应用速度更快、开发更简单、部署成本更低。
支持多向请求和响应,可以省掉对 HTTP1.x 限制所费的那些工作,比如拼接文件、图片精灵、域名分区。类似地,通过减少 TCP 连接的数量,HTTP2.0 也会减少客户端和服务器的 CPU 及内存占用。

请求优先级

把 HTTP 消息分解为很多独立的帧之后,就可以通过优化这些帧的交错和传输顺序,进一步提升性能。为了做到这一点,每个流都可以带有一个 31 比特的优先值:

0 表示最高优先级; (2^31)-1 表示最低优先级。

有了这个优先值,客户端和服务器就可以在处理不同的流时采用不同的策略,以最优的方式发送流、消息和帧。具体来讲,服务器可以根据流的优先级,控制资源分配(CPU、内存、带宽),而在响应数据准备好之后,优先将高优先级的帧发送给客户端。
浏览器在渲染页面时,并非所有资源都具有相同的优先级:HTML 文档本身对构建 DOM 不可或缺,CSS 对构建 CSSOM 不可或缺,而 DOM 和 CSSOM 的构建都可能会受到 JavaScript 资源的阻塞,其他资源(如图片)的优先级都可以降低。为加快页面加载的速度,所有现代浏览器都会基于资源的类型以及它在页面中的位置排定请求的优先次序,甚至通过之前的访问来学习优先级模式–比如,之前的渲染如果被某些资源阻塞了,那么同样的资源在下一次访问时可能就会被赋予更高的优先级。

每个来源一个连接

有了新的分帧机制后,HTTP2.0 不再依赖多个 TCP 连接去实现多流并行了。现在,每个数据流都拆分成很多帧,而这些帧可以交错,还可以分别优先级。于是,所有 HTTP2.0 连接都是持久化的,而且客户端与服务器之间也只需要一个连接即可。
每个来源一个连接显著减少了相关资源的占用:连接路径上的套接字管理工作量少了,内存占用少了,连接的吞吐量大了。此外,从上到下所有层面上也都获得了相应的好处:

  • 所有话剧流的优先次序始终如一;

  • 压缩上下文单一使得压缩效果更好;

  • 由于 TCP 连接减少而使网络拥塞状况得以改观;

  • 慢启动时间减少,拥塞和丢包回复速度更快。

流量控制

在同一个 TCP 上传输多个数据流,就意味着要共享带宽。标定数据流的优先级有助于按序交付,但只有优先级还不足以确定多个数据流或多个连接间的资源分配。为解决这个问题,HTTP2.0 为数据流和连接的流量控制提供了一个简单的机制:

  • 流量控制基于每一跳进行,而非端到端的控制;

  • 流量控制基于窗口更新帧进行,即接收方广播自己准备接收某个数据流的多少字节,以及整个连接要接收多少字节;

  • 流量控制窗口大小通过 WINDOW_UPDATE 帧更新,这个字段指定了流 ID 和窗口大小递增值;

  • 流量控制有方向性,即接收放可能根据自己的情况为每个流乃至整个连接设置任意窗口大小;

  • 流量控制可以由接收方禁用,包括针对个别的流和针对整个连接。

HTTP2.0 建立连接之后,客户端与服务器交换 SETTINGS 帧,目的是设置双向的流量控制窗口大小。除此之外,任何一端都可以选择禁用个别流或整个连接的流量控制。

服务端推送(server push)

HTTP2.0 新增的一个强大的新功能,就是服务器可以对一个客户端请求发送多个响应。换句话说,除了对最初请求的响应外,服务器还可以额外向客户端推送资源,而无需客户端明确的请求。
建立 HTTP2.0 连接后,客户端与服务器交换 SETTINGS 帧,借此可以限定双向并发的流的最大数量。因此,客户端可以限定推送流的数量,或者通过设置为 0 而完全禁用服务器推送。
所有推送的资源都遵守同源策略。换句话说,服务器不能随便将第三方资源推送给客户端,而必须是经过双方确认才行。

首部(header)压缩

HTTP 的每次通信都会携带一组首部,用于描述传输的资源及其属性。在 HTTP1.x 中这些元数据都是以纯文本形式发送的,通常会给每个请求增加 500-800 字节的负担。如果算上 Cookie,增加的负担更重。为减少这些,HTTP2.0 会压缩首部元数据。 HTTP2.0 在客户端和服务器端使用“首部表”来跟踪和存储之前发送的键值对,对于相同的数据,不再通过每次请求和响应发送; 首部表在 HTTP2.0 的连接存续期内始终存在,有客户端和服务器共同更新; 每个新的首部键值对要么被追加到当前表的末尾,要么替换表中之前的值。
于是,HTTP2.0 连接的两端都知道已经发送了哪些首部。请求与响应首部的定义在 HTTP2.0 中基本没有改变,只是所有的首部健必须全部小写。

HTTP2.0 的升级改造

HTTP2.0 其实可以支持非 HTTPS 的,但是现在主流的浏览器像 chrome,firefox 表示还是只支持基于 TLS 部署的 HTTP2.0 协议,所以要想升级成 HTTP2.0 还是先升级 HTTPS 为好。
当你的网站已经升级 HTTPS 之后,那么升级 HTTP2.0 就简单很多,如果你使用 NGINX,只要在配置文件中启动相应的协议就可以了,可以参考 NGINX 白皮书,NGINX 配置 HTTP2.0 官方指南 https://www.nginx.com/blog/nginx-1-9-5/。
使用了 HTTP2.0 那么,原本的 HTTP1.x 怎么办,这个问题其实不用担心,HTTP2.0 完全兼容 HTTP1.x 的语义,对于不支持 HTTP2.0 的浏览器,NGINX 会自动向下兼容的。

HTTP2.0 的多路复用和 HTTP1.X 中的长连接复用有什么区别?

HTTP/1.* 一次请求-响应,建立一个连接,用完关闭;每一个请求都要建立一个连接;
HTTP/1.1 Pipeling 解决方式为,若干个请求排队串行化单线程处理,后面的请求等待前面请求的返回才能获得执行机会,一旦有某请求超时等,后续请求只能被阻塞,毫无办法,也就是人们常说的线头阻塞;
HTTP/2 多个请求可同时在一个连接上并行执行。某个请求任务耗时严重,不会影响到其它连接的正常执行。

为什么需要头部压缩?

假定一个页面有 100 个资源需要加载(这个数量对于今天的 Web 而言还是挺保守的), 而每一次请求都有 1kb 的消息头(这同样也并不少见,因为 Cookie 和引用等东西的存在), 则至少需要多消耗 100kb 来获取这些消息头。HTTP2.0 可以维护一个字典,差量更新 HTTP 头部,大大降低因头部传输产生的流量。

HTTP2.0 多路复用有多好?

HTTP 性能优化的关键并不在于高带宽,而是低延迟。TCP 连接会随着时间进行自我「调谐」,起初会限制连接的最大速度,如果数据成功传输,会随着时间的推移提高传输的速度。这种调谐则被称为 TCP 慢启动。由于这种原因,让原本就具有突发性和短时性的 HTTP 连接变的十分低效。HTTP/2 通过让所有数据流共用同一个连接,可以更有效地使用 TCP 连接,让高带宽也能真正的服务于 HTTP 的性能提升。