HTTP/2:提升网页加载速度的关键升级

225 阅读19分钟

最近在重温HTTP2相关的知识,发现 HTTP/2 已经逐渐成为提升前端性能的关键。为了查漏补缺、夯实基础,我决定重新梳理一下 HTTP 相关的知识。今天,想和大家一起聊聊 HTTP/2,这个相较于 HTTP/1.1 带来了诸多优化的版本。它不仅加快了网页加载速度,还大幅提升了资源的利用效率。接下来,让我们一起看看 HTTP/2 的核心特性,以及它是如何帮助前端开发变得更加高效的。

为什么是 HTTP/2,而不是 HTTP/2.0?

在刚接触 HTTP/2 的时候,看到HTTP2的字眼不禁有这样的疑惑:为什么直接叫 HTTP/2,而不是我们习惯的 HTTP/2.0?少了个“点零”,好像少了点熟悉的味道。翻阅资料才发现,开发者们可是有意为之,毕竟 HTTP/2 并不只是对 HTTP/1.1 的简单升级,而是一场彻底的性能革新。

HTTP/2 引入了多路复用、头部压缩、服务器推送等一系列新技术,直接提升了数据传输的效率。这次改进幅度这么大,开发者觉得没必要再给它加个“点零”,像个小修补似的。HTTP/2 这个名字更能体现它带来的跨越式变革——告别过去的“1.x”时代,直接迈入性能升级的新纪元。

总的来说,少了“点零”反而更简洁利落,也正好表明了:这是一次彻底的进化,而不是微调。所以,这一次的 HTTP/2,绝对是大刀阔斧的改进,而不仅仅是一个小版本更新。

不过,虽然名字变得简洁干练,但有些人可能会担心:HTTP/2HTTP/1.1 还能“好好相处”吗?毕竟,很多系统还在使用 HTTP/1.1。那么,它们能顺利兼容吗?接下来我们就聊聊 HTTP/2HTTP/1.1 的兼容性问题

HTTP/2 依旧兼容 HTTP/1

上面提到 HTTP/2 是一场性能革命,那问题来了:它和我们熟悉的 HTTP/1.1 能和平共处吗?答案是:必须能!HTTP/2 非常“包容”,它完全兼容 HTTP/1.1,就像一个善解人意的新同事,不会强行要求所有系统立刻升级,而是能够跟“老前辈” HTTP/1.1 一起继续愉快工作。

HTTP/2 保留了 HTTP/1.1 的核心语义,比如请求方法、状态码、URI 以及头部字段等。这意味着,即使客户端或服务器只支持 HTTP/1.1,双方仍能正常通信。其实,HTTP/2 的出现并不是为了彻底替代 HTTP/1.1,而是为了改进数据传输方式,让网络性能更高效,但不需要抛弃现有系统。这种兼容性让我们在逐步过渡到 HTTP/2 的过程中不至于手忙脚乱。

而且,HTTP/2 可以在客户端和服务器之间自动协商使用的协议版本。如果一方不支持 HTTP/2,没问题,它会自动回退到 HTTP/1.1 继续进行通信。这就好比用新手机打电话时,发现对方用的是老款手机,通话质量可能略有差别,但两边依然能顺利聊天。对于开发者来说,这种平滑过渡极大降低了升级的压力,让我们能逐步享受 HTTP/2 带来的好处。

所以,无论你是支持 HTTP/2 的“前沿派”,还是还在 HTTP/1.1 阶段的“传统派”,都可以放心,两者可以在同一个网络中“和谐共处”,毫无压力。

当然,兼容 HTTP/1.1 是一种让过渡变得更轻松的手段,但 HTTP/2 的真正厉害之处还在于它的性能优化,其中一大亮点就是头部压缩。那么,接下来我们就聊聊这个让 HTTP/2 更加高效的“瘦身秘笈”——头部压缩。

头部压缩

说到 HTTP/2 的新特性,头部压缩绝对值得一提。毕竟,在 HTTP/1.1 时代,每次请求都会携带大量重复的头部信息。我们可以设想一下这样的场景: 每次你点开网页,它都要重复自我介绍一遍,什么 User-AgentCookieHost,有时候这些信息一模一样,却每次都要传一遍。别说服务器了,浏览器自己可能都觉得累!

为了解决这个问题,HTTP/2 引入了头部压缩(Header Compression),帮 HTTP 报文减掉了这不必要的“赘肉”。它通过一种叫 HPACK 的算法,将头部信息压缩起来,不再每次都重复发送。要是头部字段之前发过,服务器和客户端就会“心领神会”,直接用之前的记录,省去了重复传输的烦恼。

比如,你浏览网页时,HTTP/2 会记住那些常用的头部信息,如果后续请求中有相同的头字段,HTTP/2 就不会重复传输,而是使用前面的压缩值。这就像打包好的“关键词”,双方都知道,传递的只是一种编号而不是完整的文本。整个过程不仅加快了传输速度,还大大节省了带宽资源。

最重要的是,这种头部压缩机制还通过静态表动态表进行高效管理。静态表存储的是标准化的常见头部字段,而动态表则根据实时传输过程中产生的数据进行更新。换句话说,HTTP/2 会聪明地“记住”哪些信息已经传过,避免重复工作。下面我们来看看静态表和动态表的具体原理和细节.

静态表 是 HTTP/2 规范中定义的一个固定的、不可改变的表,它包含了常见的 HTTP 头部字段,如 :method:status 等。这些字段的索引和内容在客户端和服务器中是完全相同的。

  • 获取静态表:静态表不是在通信过程中动态生成或获取的,而是事先定义好的。无论客户端还是服务器,都会在实现 HTTP/2 协议时预先加载这份静态表。
  • 静态表的内容:它是标准的一部分,因此客户端和服务器都直接在它们的 HTTP/2 库中包含了相同的静态表。客户端从 HTTP/2 的实现一开始就拥有静态表,并且在请求和响应中通过预定义的索引来引用这些常见头部字段。

动态表

  • 动态表是每一方在通信过程中动态维护的一个表,用于记录最近使用的头部字段。动态表可以随着通信的进行,逐步记录新的头部字段,从而在后续请求中引用这些字段以节省带宽。
  • 动态表存在于浏览器端和服务器端,它们各自有独立的版本。客户端和服务器会在 HTTP/2 通信中不断更新动态表,但它们各自维护自己独立的表,并且是相互同步的。
  • 当客户端向服务器发起请求,或服务器向客户端发送响应时,动态表会根据发送的头部信息进行更新。如果某个头部字段被添加到动态表中,那么在后续请求中,双方可以通过索引来引用这些字段,从而减少传输的数据量。

总之,HTTP/2 的头部压缩功能让数据传输更加高效,就像给 HTTP 报文来了一次“瘦身训练营”,在减少冗余数据的同时,让浏览器和服务器的沟通更加轻快、流畅。

提示: HPACK 就像是我们经常用顺丰快递寄东西的“常客模式”。想象每次寄顺丰,你总是要填写相同的发件人、收件人信息,这样既浪费时间又麻烦。于是,顺丰推出了一个“常用地址本”,你可以把这些信息存下来,之后每次寄件只要选一下地址就好了。 > > HPACK 在 HTTP/2 中的工作方式就类似于这种常用地址本,它通过“字典”来保存常见的 HTTP 头部信息。这样每次请求都不用重复发送相同的头部,而是用一种更简洁的编码方式来表示,类似于用地址本的编号代替详细地址信息,从而显著减少数据传输的大小,提高传输效率。

HTTP/2 的“语言升级”——二进制格式

在 HTTP/1.1 时代,数据是通过纯文本格式传输的,虽然看起来“人类友好”,但在计算机的眼中,处理起来有点费劲。因为机器对“自然语言”那可是有点笨拙。所以到了 HTTP/2,这个问题就被彻底解决了——它改用二进制格式来传输数据。

说到二进制格式,大家都知道这是机器的母语,0 和 1 是它们最熟悉的语言。HTTP/2 通过这个“语言升级”,让服务器和客户端的对话效率大大提升。不管是请求还是响应,所有数据都被转换成了二进制帧进行传输。换句话说,HTTP/2 不再让计算机去费力解析文本,而是直接用二进制流,这不仅让数据传输更快,处理也更加精准。

为什么要换成二进制呢?因为相比文本格式,二进制更紧凑、更高效,少了那些“繁琐的ASCII字母”,大大减少了数据传输时的开销,性能自然飞速提升。而且,原来使用纯文本的时候容易出现多义性,比如大小写、空白字符、回车换行、多字少字等等, 二进制格式能够避免各种由于文本解析产生的错误,让通信更加稳健和可靠。

二进制格式也是 HTTP/2 在性能上大刀阔斧做出改进的一个关键点,通过这个转变,HTTP/2 彻底摆脱了“人类语言”的束缚,进入了更加高效的“机器语言”时代。与之搭配的,还有 HTTP/2 的另一大杀手锏:多路复用,它让数据流动更为顺畅。接下来,我们就深入聊聊这个让 HTTP/2 再次飞跃的强大特性。

出于兼容的考虑,HTTP/2 延续了 HTTP/1 的“明文”特点,可以像以前一样使用明文传输数据,不强制使用加密通信,不过格式还是二进制,只是不需要解密。

为了区分“加密”和“明文”这两个不同的版本,HTTP/2 协议定义了两个字符串标识符:"h2"表示加密的 HTTP/2,"h2c"表示明文的 HTTP/2,多出的那个字母“c”的意思是“clear text”。

虚拟的“流”——多路复用的核心

上面我们提到了 “多路复用” ,这是 HTTP/2 带来的一个革命性改进。而它的核心就是所谓的虚拟“流” 。在 HTTP/1.1 中,每次请求都得占用一个单独的连接,就像排队办事一样,必须一件事做完了才能办下一件。而在 HTTP/2 中,多路复用允许多个请求在同一个连接上同时进行

我们想象这样的场景,HTTP/1.1 就像是单车道公路,一辆车堵住了,后面的车只能干等着。而 HTTP/2 则是宽敞的高速公路,虚拟的“流”就是这些车道,它们让多辆车(也就是多个请求)可以同时前进,互不干扰。每个请求和响应都被分解成更小的二进制帧,并通过一个虚拟的“流”并发传输。服务器和客户端可以随时开启或关闭新的流,数据流动更加灵活高效。

在“流”的层面上看,消息是一些有序的帧序列,像一条传送带一样,按顺序输送请求和响应中的数据。但是在“连接”的层面上,事情变得更有趣了:消息是乱序收发的帧,帧的顺序已经不再固定!这意味着多个请求/响应之间不再有严格的顺序关系,不需要排队等待,彻底解决了让 HTTP/1.1 头疼的“队头阻塞”问题。这样一来,数据的延迟大幅降低,连接的利用率也随之大幅提升。相当于让所有车道的车都可以各走各的路线,彼此毫不干扰,谁快谁先到。

更妙的是,这些流都在同一个 TCP 连接上跑,避免了 HTTP/1.1 中频繁建立和关闭连接的开销。想象一下,以前我们每次都得为每个请求“开个新店”,而现在有了多路复用,所有请求都可以在同一家“店”里同时进行处理,效率自然就提升了。

这些虚拟的“流”还能够通过优先级管理不同的请求。例如,一个大型图片下载可以放在一个较低优先级的流里,而重要的文本内容则可以优先加载,确保用户的关键数据更快到达。

简单来说,HTTP/2 的虚拟“流”让网络请求不再堵车,大大加快了数据传输效率。这个机制让我们同时进行多个请求时,不再需要等待和排队,网络性能得到了极大优化。这里我整理下流的几个特点:

  • 流是可并发的:在一个 HTTP/2 连接上可以同时发出多个流,数据并发传输。每个流都相当于一个“车道”,所以同一个连接可以处理多个请求,真正实现了“多路复用”。

  • 流是双向的:不仅客户端可以创建流,服务器也能创建流,双方各自独立运行,互不干扰。这个双向机制让 HTTP/2 的通信更加灵活,可以更高效地处理复杂的交互场景。

  • “请求 - 应答”的来回:在一个流里面,客户端和服务器可以同时发送或接收数据帧。也就是说,一个请求与响应的数据交换可以在同一个流里双向传输,就像一场即时对话。

  • 流之间独立:流与流之间是彼此独立的,互相没有依赖关系。然而,流内部的帧顺序却是严格固定的,这确保了数据的正确处理。

  • 流可以设置优先级:HTTP/2 支持为流设置优先级。比如,可以优先处理重要的 HTML 和 CSS 文件,推迟传输图片资源,从而优化用户体验,让网页更快渲染出来。

  • 流 ID 规则:每个流都有一个唯一的 ID。客户端发起的流 ID 是奇数,服务器端发起的是偶数,而且流 ID 不能重用,只能递增,确保每个流都唯一标识。

  • 终止流:在传输数据的过程中,客户端和服务器可以通过发送 RST_STREAM 帧随时终止某个流,取消传输。这就像随时挂断一场通话,确保双方不会浪费不必要的资源。

  • 第 0 号流的特殊性:HTTP/2 中,第 0 号流是个例外,它不能关闭,也不能传输数据帧。它的唯一作用是传输控制帧,用于进行流量控制,比如管理整个连接上的流量分配。

除此之外,HTTP/2 还在一定程度上改变了传统的“请求 - 应答”工作模式。服务器不再只是被动等待请求,还可以主动新建“流”,向客户端发送消息。比如,当浏览器刚请求 HTML 时,服务器就提前把可能需要的 JS、CSS 文件推送给客户端,减少后续请求的等待延迟。这就是我们常说的**“服务器推送”(Server Push,也叫 Cache Push)**,让客户端可以更快加载所需资源,进一步提升用户体验。

“流”是虚拟的,实际上并不存在,所以 HTTP/2 就可以在一个 TCP 连接上用“”同时发送多个“碎片化”的消息,这就是常说的“多路复用”( Multiplexing)——多个往返通信都复用一个连接来处理。

协议栈——HTTP/2 的“地基”

谈到 HTTP/2,我们不能不提到背后的技术基础——协议栈。这就像建造一座高楼,HTTP/2 再强大,它也离不开坚实的地基,而这个“地基”就是协议栈。HTTP/2 运行在传输层(TCP)之上,而传输层再下去是网络层、数据链路层,最终到底层的物理层。每一层协议都有各自的职责,就像每层楼都有不同的功能,最终共同支撑起这座网络“大楼”。我们来看看下图HTTP/1, HTTPS和HTTP/2的协议栈对比

HTTP/2 是应用层协议,负责处理客户端和服务器之间的请求和响应。但它的“邻居们”也很关键,尤其是下方的 传输层协议(TCP) 。TCP 负责确保数据能从 A 点稳妥传到 B 点,既不丢包也不会乱序。HTTP/2 和 TCP 的搭配就像司机和车,HTTP/2 是指挥方向的司机,TCP 则是让车子平稳运行的底盘和引擎。

那么,HTTP/2 和之前的 HTTP/1.1 在协议栈上的关系有什么不同呢?其实在栈的结构上没有太大的变化,它依然运行在 TCP 之上。但是,HTTP/2 的多路复用、头部压缩等特性让它在同一个连接上能处理更多的数据,大大优化了传输效率,这使得底层协议栈的利用率得到了显著提升。我们来看看下图对比

协议栈本身就像是网络通信中的传送带,HTTP/2 通过在传送带上优化数据处理方式,让同样的“货物”能够更快更稳地到达目的地。再加上 服务器推送 等新特性,HTTP/2 可以更主动地处理数据传输,进一步增强了协议栈的整体效率。

安全不再是选择题——HTTP/2 强化安全

在 HTTP/2 的世界里,安全性不是一个“可选项”,而是标配。HTTP/2 大幅提升了网络性能的同时,也特别注重安全问题。相比老前辈 HTTP/1.1,HTTP/2 强制要求使用 TLS,而且最低标准是 TLS 1.2

这意味着 HTTP/2 下,数据在传输过程中都必须经过加密,确保黑客无法轻易窥探或篡改数据内容。同时,很多不安全的密码套件在 HTTP/2 中已经被禁用,为通信增加了一层额外的保护伞。用 HTTP/2,不仅是为了速度快,更是为了在网络上保证安全、稳妥。

如果你还在用 HTTP/1.1 并且觉得加密与否无所谓,那么 HTTP/2 会直接对你说:“加密是必须的,不再讨论!” 这意味着通过 HTTP/2 传输的每个请求,都比过去的 HTTP/1.1 更加安全。

总的来说,HTTP/2 不仅给我们带来了速度的提升,还确保了数据传输的安全性。因此,选择 HTTP/2,不仅是一次性能的飞跃,也是让网站和数据更加难以攻破的一步。

实践出真知——通过亲自抓包体验 HTTP/2

光讲理论不过瘾,还是要动手实践才能真正理解 HTTP/2 的强大之处!接下来,我来使用 curl 来演示下具体的请求过程,亲眼看看 HTTP/2 的各种特性是如何在实际网络传输中发挥作用的。毕竟,纸上谈兵远不如亲身体验,HTTP/2 的头部压缩、二进制帧和虚拟流,都可以通过抓包来具体观察。

这里我们以淘宝官网为例, 我们来看看这个命令和它的执行输出:

curl -v https://uland.taobao.com

Oct-17-2024 17-49-52hq.gif

执行过程解析

1. 建立连接
命令一执行,首先会尝试与本地服务器的 3002 端口建立连接:

*   Trying 106.11.23.122:443...
* Connected to uland.taobao.com (106.11.23.122) port 443 (#0)

连接成功后,curl 会与服务器进行 ALPN(应用层协议协商) ,用于确定使用的协议版本,最终服务器选择了 HTTP/2 进行通信:

* ALPN: offers h2,http/1.1
* ALPN: server accepted h2

2. TSL握手

因为我们使用的是 HTTPS,接下来便是 TLS 握手阶段,curl 和服务器将通过 TLS 1.3 进行安全通信:

* TLSv1.3 (OUT), TLS handshake, Client hello (1):
* TLSv1.3 (IN), TLS handshake, Server hello (2):
* TLSv1.2 (IN), TLS handshake, Certificate (11):
* TLSv1.2 (IN), TLS handshake, Server key exchange (12):
* TLSv1.2 (IN), TLS handshake, Server finished (14):
* TLSv1.2 (OUT), TLS handshake, Client key exchange (16):
* TLSv1.2 (OUT), TLS change cipher, Change cipher spec (1):
* TLSv1.2 (OUT), TLS handshake, Finished (20):
* TLSv1.2 (IN), TLS handshake, Finished (20):
* SSL connection using TLSv1.2 / ECDHE-ECDSA-AES128-GCM-SHA256

这个过程确保了通信的加密性。注意这里的握手信息,如 Server helloCertificate 等,说明了服务器的证书和加密算法。

3. HTTP/2 请求发出

成功建立加密连接后,curl 发送了一个 GET 请求给 /api/test,并且我们可以看到 HTTP/2 的请求头部

* h2h3 [:method: GET]
* h2h3 [:path: /]
* h2h3 [:scheme: https]
* h2h3 [:authority: uland.taobao.com]
* h2h3 [user-agent: curl/8.0.1]
* h2h3 [accept: */*]

这些以 h2h3 开头的行表示 HTTP/2 帧 中的请求头部,传输的是二进制格式。我们可以看到 :method: GET:path: /api/test 这些是 HTTP/2 使用的伪头部字段,方便二进制传输。

4. 收到服务器响应

接下来,服务器返回了一个 200 状态码,说明请求成功。这里我们可以看到服务器返回的响应头部:

< HTTP/2 200
< server: Tengine
< date: Thu, 17 Oct 2024 09:49:35 GMT
< content-type: text/html;charset=UTF-8
< content-length: 881

这说明服务器正确处理了请求,并且返回了一个简单的 HTML 内容,长度为 13 字节。HTTP/2 响应的头部也被压缩过,因此整体传输更高效。

总结

  • HTTP/2 这次的版本号很简洁,直接跳过了小版本号,没有 2.0,体现了这次更新的重大意义。
  • HTTP/2 与 HTTP/1 保持兼容性,保留了大家熟悉的请求方法、URI 等概念,因此升级时无需担心兼容问题。
  • 采用了 HPACK 压缩算法,减少了头部字段中的重复内容,提升了带宽利用效率。
  • 在 HTTP/2 中,数据不再是简单的 Header + Body 形式,而是被切割成了多个二进制帧,并通过流传输。
  • 虚拟流多路复用 的引入,彻底解决了以往的“队头阻塞”问题,大大提高了连接效率。
  • HTTP/2 还加强了安全性,至少要求使用 TLS 1.2,并禁用了过时和不安全的加密套件。