HTTP/2 概览

HTTP/2 概览

Dec 25, 2021
计算机网络, 系统设计, 算法, 浏览器

HTTP/2 的背景 #

HTTP/2 协议发布于 2015 年,距离上一个版本 HTTP/1 的发布已经过去了 20 多年,20 年里,随着互联网规模的指数级增长,网络已经成为人们生活的一部分,各种应用对响应要求也越来越严苛,HTTP/1 根本上的性能问题也随之暴露出来。

虽然这 20 年程序员们在 HTTP/1 的基础上开发出了很多“奇技淫巧”来降低 HTTP/1 的损耗,减少能减少的请求连接数,但是都没有从根本解决问题。

HTTP/1 中,如果想并发多个请求,必须使用多个 TCP 连接,且浏览器为了控制总体资源占用,还会对单个域名有 6 个连接的请求限制(如下图)。开启多个 HTTP 连接的主要问题是,没有充分利用底层的 TCP 协议,这个问题在需要请求大量静态资源的网站会更明显。另外由于 TCP 是一种可靠的协议,在 TCP 中,建立连接会要求三次握手,同时发送数据包时会附一个序列号,如果序列中哪个数据包丢了,还会要求重发。而在 HTTP/2 中,通常只需要建立一次 TCP 连接就可以,后续的请求都可以复用这个连接。

browser request

Google 在 2009 发布了 SPDY 的新协议,SPDY 基于 HTTP 构建,没有从根本上改变协议。就像 HTTPS 封装了HTTP,但是不改变它的底层机制,SPDY 就是 HTTP/2 的前身。

虽然 HTTP/2 的 RFC 标准草案没有要求必须使用 HTTPS,但是在浏览器厂商的实现上,几乎都要求了必须在 HTTPS 之上使用 HTTP/2 。据统计 HTTPS 握手和加密过程的性能损耗与 HTTP/2 的性能提升相比,损耗可以忽略,同时支持 HTTPS 可以提供更安全的服务,可以说利远大于弊。

HTTP/2 的特性 #

下面介绍几个 HTTP/2 最主要的特性。

二进制协议 #

HTTP/2 是一个二进制的、基于数据包的协议,而 HTTP/1 是完全基于文本的。HTTP/2 的 HTTP 消息被分成清晰定义的数据帧发送,所有的 HTTP/2 消息都使用分块的编码技术。

除了 Wireshark,在 Chrome 上也可以简单的抓包看一下帧到底长什么样:访问 chrome://net-export,点击 “Start Logging to Disk”选择文件地址并开始记录 log,停止记录后通过 https://netlog-viewer.appspot.com 里 import 指定的 log 文件地址导入 log 文件。

https://raw.githubusercontent.com/tcitry/Pictures/master/uPic/L2OQ6u.png

与 TCP/IP 的结构类似,每个 HTTP/2 帧由一个固定长度的头部和不定长度的负载组成。帧是同时发送多个消息的关键,一个流(Stream)可能对应了多个帧(Frame),每个帧都有标签表明它属于哪个流(消息)。

    +-----------------------------------------------+
    |                 Length (24)                   |
    +---------------+---------------+---------------+
    |   Type (8)    |   Flags (8)   |
    +-+-------------+---------------+-------------------------------+
    |R|                 Stream Identifier (31)                      |
    +=+=============================================================+
    |                   Frame Payload (0...)                      ...
    +---------------------------------------------------------------+

多路复用 #

HTTP/2 允许在单个连接上同时执行多个请求,每个 HTTP 请求或响应使用不同的流(不同的 stream_id,请求的流 🆔 为奇数,响应的流 🆔 为偶数)。通过使用二进制分帧层,给每个帧分配一个流标识符,以支持同时发出多个独立请求。当接收到该流的所有帧时,接收方可以将帧组合成完整消息,这样在一个连接上就可以同时有两个、三个甚至上百个消息(如下图)。

https://raw.githubusercontent.com/tcitry/Pictures/master/uPic/GodmxE.png

流量控制 #

如果接收方处理消息的速度慢于发送方,就会存在积压,需要将数据放入缓冲区。而当缓冲区满时会导致丢包,需要重新发送。在连接层,TCP 支持限流,但 HTTP/2 要在流的层面实现流量控制。

HTTP/2 中的流量控制和 TCP 方法类似。在连接开始时,确定流量控制窗口大小。然后每次都会从总量中减去发送的数据的大小,而后再将接收到的响应数据大小加回去。有一个连接层的流量控制窗口,它有点类似 TCP 流量控制窗口,而且每个流也有一个流量控制窗口。发送方能发送的数据最大值不超过最小的流量控制窗口的大小。当流量控制窗口大小变为 0 的时候,发送方必须停止发送数据,直到接收到响应消息,告诉你其已将窗口大小更新为非 0 值。

流优先级 #

优先级可以避免不重要的数据在页面加载时过于占用流量,之前的优先级是通过浏览器限制请求的数量来实现的,也可以说没有实际的优先级逻辑关系,在 HTTP/2 中没有请求数量这个限制了,那么 HTTP/2 要通过自己的机制来实现。流优先级的控制力比 HTTP/1 强大,当数据帧在排队时,服务器会给高优先级的请求发送更多的帧。

HTTP/2定义了两种不同的方法来设置优先级:流依赖和流权重,可以在请求的 HEADERS 帧中设置这些优先级,或者在其他时间使用单独的 PRIORITY 帧来设置。

首部压缩 #

在很多业务场景中,同一网站下的很多交互接口传递的都是重复的一些 Header 和 Cookie 信息,这在 HTTP/2 中也会得到改善。HTTP/2 使用基于查询表和Huffman编码 HPACK 规范实现首部压缩。它以高效的方法压缩 HTTP/2 首部,节省了大量空间,特别是在请求端,因为请求时首部数据一般占比都很大。

服务端推送 #

它允许服务端给一个请求返回多个响应。

连接建立 #

HTTP/2 使用 HTTPS 协议在握手层的协议协商阶段进行确认是否支持 HTTP/2。

HTTP/2 的缺点 #

HTTP/2 需要服务器和客服端都支持,客户端好说,一般都是各大浏览器厂商,技术实力雄厚,但是服务端就比较困难了,需要有专门熟悉 HTTP/2 和 HTTPS 的人员使用 Nginx 或 Apache 等 Web 服务器进行配置。同时升级 Web 服务器是一个复杂且高危的行为,也需要评估风险。

当前我对 HTTP/2 的看法 #

HTTP/2 固然解决了 HTTP 1.1 目前的一些问题,不过我个人不建议投入精力学习细节,一方面 HTTP/3 要来了,连底层的 TCP 协议都要替换,我通过 Wireshark 抓包发现 Chrome 里已经有不少 QUIC 的流量了,Chrome 的控制台甚至已经添加了 protocol 为 h3 的标志来表示 HTTP/3 请求,所以我们可以再等等(莫非我是等等党)。

没错,HTTP/3 要来了。记得以前面试官经常问:UDP 协议是可靠的吗?我们一般都会回答是不可靠的,可是现在不一定了。HTTP/3 中将弃用 TCP 协议,改为使用基于 UDP 协议的 QUIC 协议实现,即基于 UDP 也可以有可靠协议了。由于 HTTP/2 在单个 TCP 连接上使用了多路复用,受到 TCP 拥塞控制的影响,少量的丢包就可能导致整个 TCP 连接上的所有流被阻塞,HTTP/3 主要就是为了解决 HTTP/2 中存在的队头阻塞问题。

QUIC 取代了 TCP 提供的大多数功能(创建连接、可靠性和拥塞控制部分),取代了 HTTPS 的全部(降低了创建延迟),甚至还有 HTTP/2 的一部分(流量控制和首部压缩)功能。

当然,如果你是 gRPC 的用户,最好还是深入看一看。

总结 #

如果需要继续深入研究 HTTP/2,中文资料还可以参考 halfrost 的博客,从时间线上看,这位大佬从 HTTPS 到 HTTP/2 前前后后花了半年的时间来深入研究,如果再加上密码学基础的东西,其实已经用了一年的时间,所以这套内容总体来说还是很多的,由于我的实际工作场景并没有涉及这些,也就没有继续深入展开,不过暂时也够满足我的好奇,文章的标题也就只能叫“概览”了。

参考 #

本文共 2434 字,上次修改于 Sep 2, 2022,以 CC 署名-非商业性使用-禁止演绎 4.0 国际 协议进行许可。

相关文章

» HTTPS 的 SSL/TLS 协议

» HTTPS 的密码学基础

» 跨域相关问题

» 浏览器中的 HTTP 缓存使用策略

» 实现限流的几种方案