1. 概述
在上一篇文章中,我们了解了HTTP2.0协议的基本概念以及Go中HTTP2.0的实现,并了解了请求报文的格式,本文将继续深入探讨HTTP2.0中的数据帧和流控制。
2. 数据帧
数据帧是HTTP2.0中传输数据的最基本单位,它们将特定类型的数据分割成小块,并通过HTTP2.0传输。每个数据帧都有自己的帧头。下面是帧头的结构:
+-+-------------------------------------------------------------+
|C| Length (24) (~16.7m) |
+-+-------------------------------------------------------------+
|R| Stream Identifier (31) |
+=+=============================================================+
| Frame Payload (0...) |
+---------------------------------------------------------------+
其中,帧头包括以下字段:
C:标志位,1位,用于指示该帧是否可以被压缩。
Length:24位,表示帧负载的字节数。由于长度为24位,因此最多可以传输16.7MB的数据。
R:标志位,1位,表示该帧是否与某个流关联。
Stream Identifier:31位流ID,表示该帧所属的流。值为0表示该帧不属于任何流。
Frame Payload:数据负载,可以是任何大小的数据,最大长度由Length字段定义。
2.1 数据帧类型
HTTP2.0中定义了10种数据帧类型,每个数据帧的类型由帧头的type字段标识。下面是HTTP2.0中定义的10种帧类型:
DATA:传输数据的帧类型。
HEADERS:发送HTTP头部的帧类型。
PRIORITY:指定流的优先级顺序的帧类型。
RST_STREAM:指示流重置的帧类型。
SETTINGS:用于配置HTTP/2连接的帧类型。
PUSH_PROMISE:让服务器可以告诉客户端,将有一些数据推送过来的帧类型(HTTP/2推送)。
PING:用于测试对端的响应时间的帧类型。
GOAWAY:服务器告诉客户端,单向停止某个流程的帧类型。
WINDOW_UPDATE:将流窗口的大小更新为端点的帧类型。
CONTINUATION:使某个帧包含更多头信息的帧类型。
2.2 DATA帧
DATA帧用于传输HTTP消息的正文,在一个HTTP/2连接中,一个流可以关联多个DATA帧,每个DATA帧携带数据流的一部分信息(Payload)。传输过程中会自动执行拼接,当所有数据接收完毕时,通知上层完成数据传输。
DATA帧结构如下:
+---------------+
|Pad Length? (8)|
+---------------+-----------------------------------------------+
| Data (*) ...
+---------------------------------------------------------------+
| Padding (*) ...
+---------------------------------------------------------------+
其中,帧头包含以下字段:
Pad Length:1字节,指定后面的Padding长度。如果没有Padding,该字段也不存在。
Data:任意长度,携带的数据流。
Padding:任意长度,填充字节,可要可不要。如果存在,长度由Pad Length字段指定。
2.3 HEADERS帧
HTTP/2中的请求和响应头信息都通过HEADERS或PUSH_PROMISE帧传输。
HEADERS帧可以携带:
隐藏在请求或响应中的HTTP头信息(包括cookie,user-agent,referer等)。
PRIORITY帧,可以为流指定优先顺序。
HEADERS帧结构如下:
+---------------+
|Pad Length? (8)|
+-+-------------+-----------------------------------------------+
|E| Stream Dependency? (31) |
+-+-------------+-----------------------------------------------+
| Weight? (8) |
+-+-------------+-----------------------------------------------+
| Header Block Fragment (*) ...
+---------------------------------------------------------------+
| Padding (*) ...
+---------------------------------------------------------------+
其中,帧头包含以下字段:
Pad Length:1字节,指定后面的Padding长度。如果没有Padding,该字段也不存在。
E:标志位1位,如果为1,则表示Stream Dependency有效。
Stream Dependency:31位,依赖的流的标识符。
Weight:8位,用于指定流的权重,值范围为1~256。
Header Block Fragment:HTTP头的压缩形式。
Padding:任意长度,填充字节,可要可不要。如果存在,长度由Pad Length字段指定。
3. 流控制
在HTTP2.0中,可以同时发起多个流请求,但是这会导致带宽和内存等资源被过度占用。因此,HTTP2.0定义了流控制,以最大化利用网络资源。
3.1 流的概念
在HTTP2.0中,流是HTTP消息和数据流的基本单位。流是指在一个TCP连接上的双向数据传输路径。
HTTP/2中的流有以下特点:
流可以有关联。例如,服务器可以以流ID 1为基础创建一个流ID为3和5的新流。
流是无序的。任何流都可以并行发送,这允许先到达的流可以阻塞或发送后到达的流。
而不是使用数据包作为传输单位,HTTP/2将请求和响应分解为多个帧,然后在流上发送。
3.2 流控制实现方式
HTTP2.0引入了两种流量控制机制:连接流量控制和流流量控制。
连接流量控制作为一种机制,用于限制整个HTTP2连接上可用的资源总数,而流量控制作为另一种机制,用于限制特定HTTP2流上的资源总数。
连接流控制和流控制都使用WINDOW_UPDATE帧来通信流的可用空间。
3.3 连接流量控制
在HTTP/2中,连接流量控制使用Setting帧的SETTINGS_INITIAL_WINDOW_SIZE字符串来实现。
该设置字符串指定了连接级别的初始流控制窗口大小,即初始值为65535字节,如果需要减小该连接级别的默认值,此设置可以调整到最小1。
客户端和服务器都可以指定和更改此初始窗口大小;每个端点跟踪连接流控制器的大小,并确保它不会超过对端的初始窗口大小。一个端点可以在不限制对端的设置时自行提高自己的初始窗口大小,注意它会导致Beam (拥塞控制)因窗口大小而不得不立刻采取拥塞控制措施。
3.4 流量控制窗口
流量控制窗口是确定HTTP/2中连接或流可以传输的最大数据量的控制机制。
窗口大小是为每个端点管理的,每个端点都需要跟踪每个流的可用窗口大小。
端点在流或连接级别跟踪它们的窗口,每个端点都会发送WINDOW_UPDATE帧以在其端点的传输窗口内增加可用数据字节数。
窗口大小随着时间而改变,并在连接或单个流级别中由窗口大小发送到流过程中传输的帧。
3.5 WINDOW_UPDATE帧
WINDOW_UPDATE帧用于向发送方指示一个窗口大小的增量。
接收方通过为接收到的数据帧贡献池分配发送方发送窗口来初始化窗口大小。对于在单个流或整个连接上流动的数据,窗口的大小得到动态调整。当接收者读取数据时,它使其窗口变小;当发送者接收到WINDOW_UPDATE帧时,它会使窗口变大。
WINDOW_UPDATE帧可以带有一个32位的带符号整数,以指定应在建议流或连接上增加多少字节的接收方窗口。
4. 总结
在本文中,我们深入探讨了HTTP2.0中的数据帧和流控制机制。数据帧是HTTP2.0中传输数据的最小单位,HTTP2.0定义了10种不同类型的数据帧。流控制是HTTP2.0中限制可用资源总数的机制,通过连接流量控制和流流量控制两种机制实现。连接流量控制使用SETTINGS_INITIAL_WINDOW_SIZE,控制整个HTTP2连接上可用的资源总数。流量控制窗口用于确定连接或流可以传输的最大数据量。