文章目录
-
- http/1.1
- http/2
- 探索
-
- [1. 连接前言](#1. 连接前言)
- [2. 帧结构](#2. 帧结构)
-
- [2.1 帧类型 Type](#2.1 帧类型 Type)
- [3. 帧详情](#3. 帧详情)
-
- [3.1 SETTINGS 帧](#3.1 SETTINGS 帧)
- [3.2 WINDOW_UPDATE 帧](#3.2 WINDOW_UPDATE 帧)
- [3.3 PRIORITY 帧](#3.3 PRIORITY 帧)
- [3.4 HEADERS 帧](#3.4 HEADERS 帧)
- [3.5 DATA 帧](#3.5 DATA 帧)
- [3.6 PING](#3.6 PING)
- [3.7 GOAWAY 帧](#3.7 GOAWAY 帧)
- [3.8 RST_STREAM 帧](#3.8 RST_STREAM 帧)
- [3.9 PUSH_PROMISE 帧](#3.9 PUSH_PROMISE 帧)
- [3.10 CONTINUATION 帧](#3.10 CONTINUATION 帧)
你对http2了解多少? 你愿意在你的项目中使用http2吗?为什么?
http/1.1
从上图可以看到,是一个人在银行排队等待办理一个业务,完成后再去排队办理下一个业务。这就是http/1.1的请求过程。
http/1.1是串行的,也就是说,每个请求都是按顺序进行的,如果前面的请求没有完成,后面的请求就必须等待。
我们无法知道确定之前的顾客在干嘛,可能是在办理业务,也可能是在等待,也可能是在疯狂的聊天直到世界末日。
http/2
从上图可以看到,是一个人在银行办理多个业务,每个业务都是同时进行的。这就是http/2的请求过程。
http/2是并行的,也就是说,每个请求都是同时进行的,不需要等待前面的请求完成。
疑惑
这样高强度的并行请求,会不会导致服务器压力过大?会不会导致服务器崩溃?
其实不会,http/1.1 并没有榨干TCP协议所能提供的性能。
探索
了解http/2的中的一些细节。
1. 连接前言
在 http/2 中,连接成功后,每个端点都需要发送连接序言作为所使用协议的最终确认。
- 客户端发送
PRI * HTTP/2.0\r\n\r\nSM\r\n\r\n
作为连接序言 - 服务器端回应
SETTINGS
帧,帧的内容是服务器端的初始设置,包括最大帧大小,最大并发流数等。
需要注意的是,为了避免不必要的延迟,客户端并不会等待服务器端的回应,而是直接发送请求。
2. 帧结构
名称 | 长度 | 描述 |
---|---|---|
Length | 3 字节 | 帧负载的长度(取值范围为 214~224-1 字节),使用SETTINGS 帧可以设置更大值 |
Type | 1 字节 | 帧类型 |
Flags | 1 字节 | 特定于帧类型的标志 |
R | 1 位 | 保留位,无需设置 |
Stream Identifier | 31 位 | 流标识符 |
Frame Payload | 可变长度 | 帧有效负载,长度在 Length 字段中设置 |
2.1 帧类型 Type
名称 | ID | 描述 |
---|---|---|
DATA | 0x0 | 传输流的核心内容 |
HEADERS | 0x1 | 包含 HTTP 首部,和可选的优先级参数 |
PRIORITY | 0x2 | 指示或者更改流的优先级和依赖 |
RST_STREAM | 0x3 | 允许一端停止流(通常由于错误导致的) |
SETTINGS | 0x4 | 协商连接级参数 |
PUSH_PROMISE | 0x5 | 提示客户端,服务器要推送些东西 |
PING | 0x6 | 测试连接可用性和往返时延(RTT) |
GOAWAY | 0x7 | 告诉另一端,当前端已结束 |
WINDOW_UPDATE | 0x8 | 协商一端将要接收多少字节(用于流量控制) |
CONTINUATION | 0x9 | 用以扩展 HEADER 数据块 |
更详细的内容可以参考:
3. 帧详情
3.1 SETTINGS 帧
在 http/2 中,请求是通过流的方式进行的。
通常,在发送 连接前言
之后,客户端会发送一个 SETTINGS
帧。这个帧包含了客户端的初始设置,包括最大帧大小,最大并发流数等。
- 帧大小:默认最大值 2^14 字节 (16,384 字节),如果想要更大的帧大小,可以接收方通过
SETTINGS
帧进行设置。最高可以设置为 2^24-1 字节 (16,777,215 字节)。
示例(十六进制):
json
00 00 12 04 00 00 00 00 00 00 01 00 01 00 00 00 04 00 02 00 00 00 05 00 00 40 00
这个帧的内容是:
- 帧长度:18
- 帧类型:4 (SETTINGS)
- 标志位:0
- 流ID:0
- 帧有效负载:
00 01 00 01 00 00 00 04 00 02 00 00 00 05 00 00 40 00
关于
R
位,是保留位,值为 0。
SETTINGS
帧的有效负载可以设置多个参数,由 16位
类型和 32位
值组成。
+-------------------------------+
| Identifier (16) |
+-------------------------------+-------------------------------+
| Value (32) |
+---------------------------------------------------------------+
类型 | 描述 |
---|---|
0x1 | 允许发送方通知远程端点用于解码标头块的标头压缩表的最大大小(以八位位组为单位)。 |
0x2 | 此设置可用于禁用服务器推送 |
0x3 | 指示发送方允许的并发流的最大数量 |
0x4 | 指示发送方用于流级流量控制的初始窗口大小(以八位字节为单位) |
0x5 | 指示发送方愿意接收的最大帧有效负载的大小(以八位位组为单位)。 |
0x6 | 此建议设置通知对等方发送方准备接受的标头列表的最大大小(以八位位组为单位)。 |
`SETTINGS` 帧的流ID一般为0,表示这是一个连接级的设置,而不是针对某个流的设置。
3.2 WINDOW_UPDATE 帧
在 http/2 中,流量控制是通过 WINDOW_UPDATE
帧来进行的。
WINDOW_UPDATE
帧的有效负载是一个 1
位的 R
位,和 31
位的 增加窗口大小
。
+-+-------------------------------------------------------------+
|R| Window Size Increment (31) |
+-+-------------------------------------------------------------+
帧类型是 0x8
,表示这是一个 WINDOW_UPDATE
帧。
帧标志位有两种,分别是 流流控窗口和连接流控窗口,如果是流流控窗口,则指示受影响的流,如果是连接流控窗口,流标识符为 0x0
。
示例(十六进制):
json
00 00 04 08 00 00 00 00 00 00 bf 00 01
3.3 PRIORITY 帧
PRIORITY
帧(帧类型 0x2
)用于指示或更改流的优先级和依赖。
使用 `PRIORITY` 帧时,流标识符不能为 `0x0`。
PRIORITY
帧的有效负载是一个 1
位的 E
位,和 31
位的 Stream Dependency
,和 8
位的 Weight
。
+-+-------------------------------------------------------------+
|E| Stream Dependency (31) |
+-+-------------+-----------------------------------------------+
| Weight (8) |
+-+-------------+
E
位:如果为0
,则当前流的依赖关系被视为非独占的,这意味着当前流与依赖于同一流的其他流共享依赖关系。如果为1
,则当前流的依赖被认为是独占的,当前流将独占其依赖流,所有其他原本依赖于那个流的流将改为依赖当前流。Stream Dependency
:指示当前流的依赖流的标识符。Weight
:指示当前流的权重,取值范围为1~256
。
示例(十六进制):
json
00 00 05 02 00 00 00 00 07 00 00 00 00 00
3.4 HEADERS 帧
HEADERS
帧(帧类型 0x1
)主要用于传输HTTP请求或响应的头部信息。
HEADERS
帧的有效负载:
类型 | 大小 | 描述 |
---|---|---|
Pad Length | 8 位 | 用于填充的字节长度(需要有 PADDED 标志) |
E | 1 位 | 指示流依赖性是排他(需要有 Priority 标志) |
Stream Dependency | 31 位 | 该流所依赖的流的 31 位流标识符(需要有 Priority 标志) |
Weight | 8 位 | 用于指示流的权重 |
Header Block Fragment | 可变 | 用于存放头部信息 |
Padding | 可变 | 用于填充的字节 |
+---------------+
|Pad Length? (8)|
+-+-------------+-----------------------------------------------+
|E| Stream Dependency? (31) |
+-+-------------+-----------------------------------------------+
| Weight? (8) |
+-+-------------+-----------------------------------------------+
| Header Block Fragment (*) ...
+---------------------------------------------------------------+
| Padding (*) ...
+---------------------------------------------------------------+
HEADERS
帧标志:
- `END_STREAM (0x1)`:指示这是流的最后一个帧。
- `END_HEADERS (0x4)`:指示这是头部块的最后一个帧。
- `PADDED (0x8)`:指示这个帧包含了填充字段。
- `PRIORITY (0x20)`:指示这个帧包含了优先级信息。
了解这个帧的结构,可以更好的理解http/2的请求过程。这里可以查看hpack压缩算法,了解更多关于头部块的内容。
https://datatracker.ietf.org/doc/html/rfc7541
3.5 DATA 帧
DATA
帧(帧类型 0x0
)用于传输流的核心内容。
+-------------+
|焊盘长度?(8)|
+------------------------++--------------------------------- --------------+
| 数据 (*) ...
+---------------------------------------- ------------------+
| 填充 (*) ...
+---------------------------------------- ------------------+
DATA
帧的有效负载:
Pad Length
:用于填充的字节长度(需要有PADDED
标志)。Data
:用于存放数据。数据量是帧有效负载减去存在的其他字段的长度后的剩余部分。Padding
:用于填充的字节。(填充设置为0
)
DATA
帧标志:
END_STREAM (0x1)
:指示这是流的最后一个帧。PADDED (0x8)
:指示这个帧包含了填充字段。
示例(十六进制):
json
00 00 0c 00 01 00 00 00 0f 7b 22 64 61 79 4e 6f 22 3a 33 30 7d
3.6 PING
PING
帧(帧类型 0x6
)用于测试连接的可用性和往返时延(RTT)。
+---------------------------------------------------------------+
| |
| Opaque Data (64) |
| |
+---------------------------------------------------------------+
PING
帧的有效负载:
Opaque Data
:用于存放数据。发送者可以包含它选择的任何值并以任何方式使用这些八位字节。
PING
帧标志:
ACK (0x1)
:指示该 PING 帧是 PING 响应。端点必须在 PING 响应中设置此标志。端点不得响应包含此标志的 PING 帧。
流标识符为 `0x0`,表示这是一个连接级的PING帧。否则,视为连接错误。
示例(十六进制):
json
00 00 08 06 00 00 00 00 00 00 00 00 00 00 00 00 00
3.7 GOAWAY 帧
GOAWAY
帧(帧类型 0x7
)用于告诉另一端,当前端已结束。
+-+-------------------------------------------------------------+
|R| Last-Stream-ID (31) |
+-+-------------------------------------------------------------+
| Error Code (32) |
+---------------------------------------------------------------+
| Additional Debug Data (*) |
+---------------------------------------------------------------+
GOAWAY
帧的有效负载:
R
:保留位,值为0
。Last-Stream-ID
:指示发送端认为的最后一个流的标识符。Error Code
:用于指示连接关闭的原因。Additional Debug Data
:用于存放额外的调试数据。
GOAWAY
帧行为:
- 发送了GOAWAY帧的一端将忽略所有在GOAWAY帧指定的最后流标识符之后启动的流的帧。
- 接收GOAWAY帧的一端不应再在该连接上开启新的流,但可以建立新的连接来发起更多的流。
- 如果接收方在流标识符比GOAWAY帧中指示的更高的流上发送了数据,这些流将不会被处理。接收方可以认为这些流就像从未被创建过一样,并可以在新的连接上重试它们。
示例(十六进制):
json
00 00 08 07 00 00 00 00 00 00 00 00 00 00 00 00 00
3.8 RST_STREAM 帧
RST_STREAM
帧(帧类型 0x3
)用于允许一端停止流(通常由于错误导致的)。
+---------------------------------------------------------------+
| Error Code (32) |
+---------------------------------------------------------------+
RST_STREAM
帧的有效负载:
Error Code
:用于指示流关闭的原因。
和`GOAWAY`相比,`GOAWAY`针对整个连接,而 `RST_STREAM` 仅针对单个流。 `RST_STREAM` 帧的流标识符不能为 `0x0`。
示例(十六进制):
json
00 00 04 03 00 00 00 00 03 00 00 00 00
3.9 PUSH_PROMISE 帧
PUSH_PROMISE
帧(帧类型 0x5
)用于提前通知对等端点发送方打算发起的流。PUSH_PROMISE 帧包括端点计划创建的流的无符号 31 位标识符以及为流提供附加上下文的一组标头。
+---------------+
|Pad Length? (8)|
+-+-------------+-----------------------------------------------+
|R| Promised Stream ID (31) |
+-+-------------+-----------------------------------------------+
| Header Block Fragment (*) ...
+---------------------------------------------------------------+
| Padding (*) ...
+---------------------------------------------------------------+
PUSH_PROMISE
帧的有效负载:
Pad Length
:用于填充的字节长度(需要有PADDED
标志)。R
:保留位,值为0
。Promised Stream ID
:指示发送方打算创建的流的标识符。Header Block Fragment
:用于存放头部信息。Padding
:用于填充的字节。
PUSH_PROMISE
帧标志:
- `END_HEADERS (0x4)`:指示这是头部块的最后一个帧。
- `PADDED (0x8)`:指示这个帧包含了填充字段。
示例(十六进制):
json
00 00 04 05 04 00 00 00 00 00 00 00 01
PUSH_PROMISE帧允许服务器主动向客户端推送资源,而不需要客户端显式地请求这些资源。这是一种服务器推送技术,旨在减少网页加载时间。通过预先发送客户端将需要的资源,服务器可以显著减少网页完成渲染所需的往返次数(RTTs)。
但是,由于服务器推送可能会导致客户端缓存不一致,因此,服务器推送的资源应该是客户端已经请求过的资源,或者是客户端可能会请求的资源。
3.10 CONTINUATION 帧
CONTINUATION
帧(帧类型 0x9
)用于扩展 HEADER
数据块。
+---------------------------------------------------------------+
| Header Block Fragment (*) ...
+---------------------------------------------------------------+
CONTINUATION
帧的有效负载:
Header Block Fragment
:用于存放头部信息。
CONTINUATION
帧标志:
END_HEADERS (0x4)
:指示这是头部块的最后一个帧。(如果未设置 END_HEADERS 位,则该帧后面必须跟有另一个 CONTINUATION 帧。)