HTTP/2 是一个应用层传输协议,是 HTTP 协议的第二个主要版本。HTTP2 主要是基于 google 的 SPDY 协议,SPDY 的关键技术被 HTTP2 采纳了,因此 SPDY 的成员全程参与了 HTTP2 协议制定过程
HTTP/2 由互联网工程任务组(IETF)的Hypertext Transfer Protocol Bis (httpbis)工作小组进行开发的,是自1999年HTTP/1.1发布后的首个更新
HTTP/2 的主要目标包括异步连接多路复用、头部压缩、请求/响应管线化等,这些功能可以提高传输效率、减少延迟、增加服务器的处理能力,以及提供更好的安全性
HTTP/2 基于 TLS/1.2 或以上版本的加密连接,简称为h2(加密连接)或h2c(非加密连接)。在开放互联网上,HTTP/2将只用于https网址,而http网址将继续使用HTTP/1.1。这种设计旨在增加使用加密技术,以提供强有力的保护去遏制主动攻击
HTTP/1.1 问题
HTTP/1.1 存在一些问题,其中最主要的问题是队头阻塞(Head-of-Line Blocking)和性能瓶颈
- 队头阻塞:在HTTP/1.1中,每个请求都需要在一个TCP连接上按照顺序进行传输。如果一个请求因为某些原因被阻塞或延迟,后面的请求也必须等待,导致队头阻塞。这会降低并发性能和响应速度
- 头部冗余:在HTTP/1.1中,每个请求和响应都包含大量的头部字段,这些字段在每个请求和响应中都需要重复传输。这导致了头部冗余和增加了网络传输的大小
- 单一连接限制:在HTTP/1.1中,每个请求都需要创建一个新的TCP连接,而创建和维护TCP连接会产生额外的开销。同时,每个连接都需要经过慢启动和拥塞控制等机制,导致性能瓶颈
- 无法主动推送资源:在HTTP/1.1中,服务器只能响应客户端的请求,无法主动推送资源给客户端。这导致了客户端需要发起多个请求才能获取所有需要的资源,增加了网络延迟和额外的请求次数
HTTP/2
HTTP/2 重要概念
- 帧(Frame):HTTP2 中最小通信数据单元,每个帧至少包含了一个标识(stream identifier,简称stream id)该帧所属的流。
- 消息(Message):消息由一个或多个帧组成。例如请求的消息和响应的消息。
- 流(Stream):存在于 HTTP2 连接中的一个“虚拟连接通道“,它是一个逻辑概念。流可以承载双向字节流,及是客户端和服务端可以进行双向通信的字节序列。每个流都有一个唯一的整数 ID(stream identifier) 标识,由发起流的一端分配给流
单个 HTTP2 连接可以包含多个同时打开的流,任何一个端点(客户端和服务端)都可以将多个流的消息进行传输。一个 TCP 连接(HTTP2 连接建立在 TCP 连接之上)里可以发送若干个流,每个流中可以传输若干条消息,每条消息由若干二进制帧组成
任何一端都可以关闭流。在流上发送消息的顺序很重要,最后接收端会把 Stream Identifier (同一个流) 相同的帧重新组装成完整的消息报文。特别是 HEADERS 帧和 DATA 帧的顺序在语义上非常重要
二进制帧层
上图可以看出,HTTP1.1 是明文文本,而 HTTP2.0 首部(HEADERS)和数据消息主体(DATA)都是帧(frame)。frame 是 HTTP2 协议中最小数据传输单元
帧 Frame 的格式
一旦建立 HTTP/2 连接,客户端和服务器就通过交换帧进行通信,帧是协议中最小的通信单元。所有帧有固定的 9 字节标头(图 12-7),其中包含帧的长度、类型、标志位字段和 31 位流标识符
字段 | 长度 | 语义 |
---|---|---|
Length | 24 bit | 表示 Frame Payload 的长度占用字节数,帧头的 9 字节不包含在 Length 值中 |
Type | 8 bit | 帧类型,决定了帧的格式和语义。实现时必须忽略或抛弃未知类型的帧 |
Flags | 8 bit | 用于特定的帧Frame类型语义 |
R(Reserved) | 1 bit | 保留字段 |
Stream Identifier | 31 bit | 流标识符,标识唯一的 HTTP/2 流 |
Frame Payload | 内容主体,由帧的类型决定 |
从技术上讲,该length字段允许每帧最多 2 24 2^{24} 224字节 (~16MB) 的有效负载。然而,HTTP/2 标准将DATA帧的默认最大有效负载大小设置为 每帧字节 2 14 2^{14} 214 (~16KB),并允许客户端和服务器协商更高的值
帧类型
帧类型,在 HTTP2 中共分为 10 种类型:
每一种类型帧都由一个 8 位类型代码来识别。每种帧类型在建立和管理整个连接或单个数据流时都有不同的作用
Type | Code(Type 值) | 说明 |
---|---|---|
DATA | 0x00 | 数据帧(type=0x00),内容主题信息Frame Payload。比如一个或多个 DATA 帧可用于传输请求或响应的信息内容 |
HEADERS | 0x01 | 头帧(type=0x01),用于打开一个流,另外还携带一个首部的块片段 |
PRIORITY | 0x02 | 优先级帧(type=0x02),在 rfc9113 中这个字段已弃用。PRIORITY 帧可以在任何流状态下发送,包括空闲或关闭的流 |
RST_STREAM | 0x03 | 流终止帧(type=0x03),允许立即终止一个流。发送 RST_STREAM 表示请求取消流或表明发生错误的情况 |
SETTINGS | 0x04 | 设置帧(type=0x04),设置两端连接方式的配置参数 |
PUSH_PROMISE | 0x05 | 推送帧(type=0x05),服务端的推送,告诉对端打算推送数据给你了 |
PING | 0x06 | PING帧(type=0x06),确定一个空闲连接是否仍然可用,也可以测量端点间往返时间(RTT)。PING 帧可以从任意端点发出 |
GOAWAY | 0x07 | GOAWAY帧(type=0x07),用于发起关闭连接的请求,或发出严重错误的信号。GOAWAY 允许端点优雅的停止接收新流,同时仍然完成对先前建立的流的处理 |
WINDOW_UPDATE | 0x08 | WINDOW_UPDATE帧(type=0x08),用于实现流控。可以作用在单独的某个流上(指定具体的 Stream Identifier ),也可以作用在整个连接上(Stream Identifier 为 |
CONTINUATION | 0x08 | 延续帧 (type=0x9),用于继续传送首部块片段字节序列 |
HTTP2 中帧 type 和 flags 可能的组合(图表中 x 符号表示该类型的帧的 flags 可以取的值)
HTTP/2 特性
多路复用
HTTP/2 引入了多路复用机制,允许在同一个TCP连接上同时发送多个请求和响应,而不需要按照顺序逐个发送。这样可以避免HTTP 1.0中的队头阻塞问题,提高了并发性能和响应速度。
从图中可以看到在 HTTP1.1 中,请求 index.html 资源,响应完毕后就关闭连接了。而在 HTTP2 中,请求完资源后,连接仍然是打开的,后面还可以继续使用这个连接通道传输数据。
上图可以看到在一个 HTTP2 connection 中,客户端和服务端双方都能够向对方发送多个流数据(stream 1、stream 3、stram 5),在 HTTP2 中用这个 stream ID 来标识帧和流的对应关系
更多关于 stream流 和 multiplexing多路复用的内容,请查看链接:https://httpwg.org/specs/rfc9113.html#rfc.section.5
头部压缩
HTTP/2 使用了专门的 HPACK 压缩算法对请求和响应的头部进行压缩,减少了传输的头部大小。这可以降低网络带宽的消耗,特别对于包含大量重复头部字段的请求非常有效
HPACK 原理:
- 客户端和服务端共同维护了一份静态字典表(Static Table),其中包含了常见头部名及常见头部名称与值的组合的代码
- 客户端和服务端根据先入先出的原则,共同维护了一份能动态添加内容的动态字典表(Dynamic Table)
- 客户端和服务端支持基于静态哈夫曼码表的哈夫曼编码(Huffman Coding)
二进制传输
HTTP/2 使用二进制格式对数据进行传输和解析,而不再使用HTTP 1.0的文本格式。这样可以提高解析效率,减少了传输数据的大小,并且更容易实现新的特性和扩展
服务端推送
服务端推送是一种在客户端请求之前发送数据的机制。在 HTTP2 中,服务器可以对客户端的一个请求发送多个响应。除了对原始请求响应外,还可以向客户端推送额外的数据。
服务端推送的目的是让服务器通过预测它收到请求后有哪些相关资源需要返回,从而减少资源请求往返次数。
比如在 HTML 页面的请求后,通常是对该页面应用的样式表和脚本的请求,当这些资源被服务端直接推送给客户端时,客户端就不需要单独给服务器发送请求来获取这些资源了
所有服务端推送数据流都由 PUSH_PROMISE 帧发起。如上图所示,在 page.html 文件中包含资源文件 script.js 和 style.css。客户端向服务端请求 page.html 文件,服务端发现 page.html 文件中包含了这两种资源文件,就会把这两种资源文件推送给客户端,以此来减少客户端的请求次数
在实践中,服务端推送很难有效使用。因为需要服务端正确预测客户端发出的额外请求,预测必须同时考虑缓存、内容协商和用户行为等因素。预测错误又可能导致性能下降,因为服务端发送了额外的数据。特别是推送了大量数据时可能与重要的响应数据发生线路争用等问题
流量控制
在 HTTP2 中,使用流来实现多路复用,这种会对 TCP 连接使用产生竞争,从而导致流传输被阻塞。流量控制是确保在同一连接上的流不会相互干扰。流量控制既能用于单个流也能用于整个连接。
HTTP2 使用 WINDOW_UPDATE 帧提供流量控制功能
-
基于流的流量控制:
- HTTP/2中的流量控制是基于每个数据流(Stream)的,而不是基于整个连接
- 每个数据流都有自己的流量控制窗口(Flow Control Window),用于控制发送方发送的数据量
- 发送方必须遵守接收方指定的流量控制窗口大小,确保不会发送超过窗口大小的数据
-
流量控制窗口的调整:
- 每个数据流都有一个初始的流量控制窗口大小,用于指示发送方可以发送的初始数据量
- 接收方可以通过发送WINDOW_UPDATE帧来调整流量控制窗口的大小,以告知发送方可以发送更多的数据
- 发送方在发送数据之前需要检查流量控制窗口的大小,确保发送的数据不会超过窗口大小
-
连接级流量控制:
- HTTP/2还引入了连接级流量控制,用于控制整个连接上的数据传输
- 连接级流量控制窗口是所有数据流窗口大小的总和,用于控制整个连接上的数据传输量
- 发送方必须同时考虑连接级流量控制窗口和每个数据流的流量控制窗口,确保不会发送超过任何一个窗口大小的数据
-
窗口更新和处理顺序:
- 接收方可以通过发送WINDOW_UPDATE帧来增加流量控制窗口的大小
- 发送方在接收到WINDOW_UPDATE帧后,会更新相应的窗口大小,并根据新的窗口大小继续发送数据
- 接收方处理WINDOW_UPDATE帧的顺序非常重要,以确保正确更新窗口大小
通过这些流量控制原则,HTTP/2可以有效地控制数据的传输量,防止一方过载导致的性能问题。流量控制机制可以调整数据流的速率,保证发送方和接收方之间的平衡,优化整个连接的传输效率
流优先级
在 HTTP2 中,一个消息可以拆分为多个单独的帧,并且允许来自多个流的帧被多路复用,客户端和服务端帧传输可能是乱序传输,所以优先级顺序就变成了一个关键性能考虑因素
HTTP2 允许每个流具有关联的权重和依赖性:
- 每个流可以分配一个 1-256 范围之间整数权重
- 每个流都可以被赋予对另外一个流的依赖性
更多流优先级信息,比如流的依赖关系、依赖权重、优先级(依赖)重排、优先级状态管理等内容,详见rfc.section.5.3文章来源:https://www.toymoban.com/news/detail-838737.html
参考资料:文章来源地址https://www.toymoban.com/news/detail-838737.html
- HTTP/2
- HTTP2 协议长文详解
- HTTP/2 FrameTypes
- NGINX HTTP/2 白皮书
到了这里,关于超越HTTP/1.1:探索HTTP/2的无尽可能性的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!