HTTP2协议介绍

前言

HTTP是现代互联网通信的基础协议之一,早在1991年,HTTP/0.9版本就诞生了,之后又陆续发布了HTTP/1.0和HTTP/1.1,为互联网应用提供了更高效和可靠的通信方式。
随着时间的推移,互联网的规模和复杂性不断扩大,HTTP/1.1的一些限制也逐渐显现出来,例如串行的请求-响应机制、头部冗余和响应阻塞等。这些问题影响了网页加载速度、性能和用户体验。
为了克服这些挑战并引领下一代Web协议的发展,HTTP2在2015年正式发布。

HTTP2

在HTTP2出现之前,HTTP一直都是一个基于文本的协议,这种协议足够简单、对人类而言具备良好的可读性,但是对于计算机而言,处理的效率就不高了。所以HTTP2在数据传输上做了大刀阔斧的改动,从一个文本协议升级为二进制协议,且带来了很多新特性。

多路复用

多路复用功能是HTTP2最重要的特性之一,它彻底改变了HTTP/1.1中的串行请求-响应模型,允许多个请求和响应同时在一个TCP连接上进行交互。
在HTTP/1.1中,一个TCP连接上同时只能有一个请求-响应,整体是串行的,尽管连接可以复用,但是如果有任何一个请求因为处理过慢或网络原因发生阻塞,就会导致队头阻塞(Head-of-line blocking或缩写为HOL),后续请求除了干等什么也做不了,TCP连接没有得到有效的利用。
为了解决这个问题,HTTP2针对每一个物理连接可以同时打开多个流Stream,消息都是基于Stream去发送的。这使得在同一个TCP连接上,可以并发进行请求-响应,资源得到更有效的利用。

Stream

Stream是逻辑上的概念,一个TCP连接上可以同时打开多个Stream,大多数Web服务的默认配置是可以同时打开100/128个Stream。
每个Stream都有一个唯一的标识符StreamID,用于区分不同的Stream。Stream的开启可以由客户端发起,也可以由服务端开启,为了StreamID不发生冲突,客户端使用奇数递增的方式,服务端使用偶数递增的方式。0号Stream比较特殊,它用来对整个TCP连接进行设置。
每个流都是独立的,有自己的流控设置,包括流的优先级、流的权重等。通过设置流的优先级,可以确保服务器在处理多个流时能够优先处理重要的请求,提高了资源的利用效率。

流量控制

HTTP1.x时代,一个连接上同时只能处理一个请求-响应,流量控制直接依赖于底层TCP协议即可,当一方缓冲区满时就会开始丢包,另一方则会停止发送数据,等待有更大的窗口再继续发送。
对于HTTP2来说,因为有多路复用,TCP的流控就显得不够用了,所以HTTP2有了自己的更精细化的流量控制。
HTTP2的流控采用滑动窗口的设计,连接建立后,双方发送的第一个Frame一定是SETTINGSFrame,用于对连接进行配置,其中一个属性SETTINGS_INITIAL_WINDOW_SIZE用于设置初试窗口大小,默认是65535字节。发送方每次发送数据都会减小窗口大小,当窗口为0时发送方必须停止发送数据,等待对方发送WINDOW_UPDATEFrame调整窗口大小后才能继续发送。
除此之外,还有流优先级控制。每个流都可以分配一个优先级,用于指示请求的重要性。优先级通过整数表示,数值越小表示优先级越高。接收方可以根据优先级来调度处理请求,优先级较高的请求将被优先处理。
通过流量控制机制,HTTP2 能够更好地适应不同网络环境和处理能力,提高传输效率和用户体验。它可以防止发送方过度发送数据导致接收方不堪重负,同时也可以确保优先处理重要请求,提高整体性能。

Frame

HTTP2在消息结构上并没有做太大的改动,对于请求/响应来说,依然有头部和消息体,只是在数据的传输上,HTTP2采用了消息分帧发送的方式。
Frame(帧)是HTTP2通信的最小单位,HTTP2定义了种不同类型的Frame,每个类型都有特定的用途和结构。

Frame类型描述
DATA传输实际的数据
HEADERS传输请求或响应的头部信息
PRIORITY指定请求的优先级
RST_STREAM取消或重置一个流
SETTINGS协商连接参数
PUSH_PROMISE服务器主动推送资源给客户端
GOAWAY告知对端连接即将关闭
WINDOW_UPDATE更新流或连接的窗口大小
CONTINUATION继续发送大型首部块,用于超过单个HEADERS帧大小限制的情况

Frame的通用结构如下:

+-----------------------------------------------+
|                 Length (24)                   |
+---------------+---------------+---------------+
|   Type (8)    |   Flags (8)   |
+-+-------------+---------------+-------------------------------+
|R|                 Stream Identifier (31)                      |
+=+=============================================================+
|                   Frame Payload (0...)                     ...|
+---------------------------------------------------------------+
字段长度(位)描述
Length24Frame有效载荷的长度(不包括Frame头的9个字节)
Type8Frame的类型
Flags8携带关于Frame的额外信息和控制选项
Reserved Bit1保留位,未使用
Stream Identifier31与Frame相关联的HTTP/2流的标识符(唯一标识一个流)
Payload可变根据Frame类型和长度不同,携带不同的信息

为了传输不同的数据,HTTP2定义了不同类型的Frame,不同类型的Frame职责明确,结构清晰,相较于HTTP/1.1使用换行符来区分头部和消息体,Frame显得更加高效且不容易出错。

头部压缩

HTTP/1.1协议中只有消息体支持压缩,头部是不支持压缩的。但现实情况是,对于每一次请求,头部信息有大量重复的数据,例如:Host、Accept、User-Agent等,这些数据几乎每次请求都不会变,但是每次都需要传输给对方,如果能将这些数据压缩传输,可以进一步提高协议的效率。
在HTTP2中,头部终于也支持压缩了,HTTP2中使用的头部压缩算法称为HPACK。HPACK使用了两个重要的机制来减小头部信息的大小:首先,它使用了静态表和动态表来存储常见的头部字段,这些字段只需发送一次,并在整个会话中重复使用;其次,HPACK使用基于差异编码的技术,将头部字段与之前发送的字段进行比较,并只发送增量部分。
静态表是在HTTP/2规范中定义的一个固定的头部字段集合,包含了常见的HTTP头部字段,例如"content-type"、"content-length"等。使用静态表时,只需发送静态表中字段的索引号,而不需要发送完整的字段名称和值。
动态表是根据实际请求和响应中出现的头部字段动态生成的,并在整个会话中进行维护。每当遇到一个新的头部字段时,会将其添加到动态表中,并为其分配一个索引号。当再次遇到相同的头部字段时,只需发送该字段的索引号和增量部分。
头部压缩不仅减小了请求和响应的大小,还显著降低了网络传输延迟和带宽消耗。同时,由于头部压缩是在传输层进行的,应用层的逻辑不需要任何更改,开发人员可以相对轻松地迁移到HTTP2协议。

服务端推送

在HTTP/1.1协议中,请求只能由客户端发起,服务端负责响应请求,服务端没法主动推送资源给客户端。HTTP2开始支持服务端推送了。
服务端推送 允许服务器在客户端请求之前将额外的资源主动推送给客户端。这意味着服务器可以预测客户端可能需要的资源并主动发送,而无需等待客户端显式请求。
例如:浏览器请求首页HTML,首页会额外依赖其它的CSS/JS等资源,此时服务端就可以直接推送这些额外的资源,浏览器下次请求时就可以直接从缓存里获取到了,减少了往返时间,提高页面的加载速度。
遗憾的是:目前主流网站对服务端推送的支持并不好,原因是很难精准的提前预测客户端要请求的资源,如果推送了客户端不需要的资源,反而是个负担。

常用的Frame

Frame是HTTP2通信的最小单位,HTTP2定义了种不同类型的Frame,这里介绍几个常见的Frame。

SETTINGS

SETTINGS Frame是客户端和服务端必须发送的第一个Frame。
TCP连接建立完毕后,双方会发送SETTINGS Frame来对连接进行配置。SETTINGS Frame结构如下:

SETTINGS Frame {
  Length (24),
  Type (8) = 0x04,

  Unused Flags (7),
  ACK Flag (1),

  Reserved (1),
  Stream Identifier (31) = 0,

  Setting (48) ...,
}

Setting {
  Identifier (16),
  Value (32),
}

前64Bit是通用结构,Payload部分是若干个键值对。一个键值对由48Bit构成,前16Bit是Key,后32Bit是Value。

ID标志位默认值说明
SETTINGS_HEADER_TABLE_SIZE0x14,096请求头表最大大小,
和HPACK压缩有关
SETTINGS_ENABLE_PUSH0x21是否允许推送
SETTINGS_MAX_CONCURRENT_STREAMS0x3-允许的最大并发流数
SETTINGS_INITIAL_WINDOW_SIZE0x465,535初始窗口大小
SETTINGS_MAX_FRAME_SIZE0x516,384最大Frame大小
SETTINGS_MAX_HEADER_LIST_SIZE0x6-头列表的最大大小

HEADERS

SETTINGS Frame发送完,连接就配置好了。接下来,就可以发起请求了。
HTTP请求从一个HEADERS Frame开始。HEADERS Frame结构如下:

HEADERS Frame {
  Length (24),
  Type (8) = 0x01,

  Unused Flags (2),
  PRIORITY Flag (1),
  Unused Flag (1),
  PADDED Flag (1),
  END_HEADERS Flag (1),
  Unused Flag (1),
  END_STREAM Flag (1),

  Reserved (1),
  Stream Identifier (31),

  [Pad Length (8)],
  [Exclusive (1)],
  [Stream Dependency (31)],
  [Weight (8)],
  Field Block Fragment (..),
  Padding (..2040),
}
  • PADDEDFlag用来设置是否有填充数据,Pad Length代表填充数据长度,Padding是填充数据,全都是0。填充数据没有任何含义,只是出于安全考虑,用来隐藏真实的消息长度。
  • PRIORITYFlag用来设置资源请求的优先级。
  • END_HEADERSFlag用来设置当前Frame包含请求的所有头部,后续没有CONTINUATION Frame了。
  • END_STREAMFlag用来结束流,代表后续没有DATA Frame了。

Tips:如果头部非常大,一个Frame塞不下,HEADERS Frame后面会紧跟一个CONTINUATION Frame,而不是多个HEADERS Frame,这点要注意。

Field Block Fragment部分解码后就是我们常见HTTP头部,如下示例:

:method: GET
:path: /
:scheme: https
:authority: www.taobao.com
accept: */*
accept-encoding: gzip, deflate
user-agent: nghttp2/1.45.1

DATA

在HTTP1协议中,头部和消息体是通过换行符来区分的,到了HTTP2是通过Frame类型区分的。
一般情况下,HEADERS Frame后面跟着的就是DATA Frame了,它用来发送消息体,结构如下:

DATA Frame {
  Length (24),
  Type (8) = 0x00,

  Unused Flags (4),
  PADDED Flag (1),
  Unused Flags (2),
  END_STREAM Flag (1),

  Reserved (1),
  Stream Identifier (31),

  [Pad Length (8)],
  Data (..),
  Padding (..2040),
}

DATA Frame极其简单,只有真实数据和填充数据。因为是二进制的,所以你可以用DATA Frame来传输任何格式的数据,可以是HTML、图片、视频、音频等等。

  • PADDEDFlag:是否包含填充数据。
  • END_STREAMFlag:流结束,当前Frame是最后一个。

关于其它Frame的定义请参考:https://httpwg.org/specs/rfc9113.html#FrameTypes

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

程序员小潘

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值