zl程序教程

您现在的位置是:首页 >  数据库

当前栏目

HTTP/2:HTTP/1.1你该进步了

2023-04-18 12:26:54 时间

摘要

  1. 兼容HTTP1.1
  2. 头部压缩
  3. 二进制帧
  4. 并发传输
  5. 服务器主动推送资源
  6. HTTP2的队头阻塞问题

兼容HTTP1.1

HTTP2的优点我们后面会一一列出,但是一个新的东西的升级必须要做到向前兼容才能快速推广,因为只有这样才能减少对用户的影响。

HTTP2对HTTP1.1的兼容体现在哪些方面

  • 协议名依然使用http,加密的依然使用https,浏览器和服务器只需要在幕后自动升级协议,用户并不会感知到协议的变化。
  • HTTP2下层协议依旧是TCP,但把HTTP分成了语法语义两个部分,语义层不做改动(比如请求方法、状态码等都保持不变)

头部压缩

HTTP1.1只能对请求体无法压缩,但HTTP2除了对请求体进行压缩还可以对Heder进行压缩。

HTTP2 Header的压缩方式

HPACK算法,HPACK算法主要包含三个部分:

  • 静态字典
  • 动态字典
  • Huffman编码

客户端和服务端会建立维护好静态字典和动态字典,用长度较小的索引号表示重复的字符串,另外一些不固定的变化的value需要通过Huffman进行编码。

静态字典

HTTP2为Head中高频出现的字符串和字段建立了一张静态表,静态表中一共维护了61项,主要包含三项内容:

  • Index:表示索引
  • Header Name:表示字段的名称
  • Header Value:表示索引对应的value

为什么有些Header Value不存在?

有些Head Value不是固定的,这些value需要经过Huffman压缩后在进行发送。

Head的格式

如果Head字段属于静态字典,那么Head的固定格式如下:

  • 第一个字节:前两位固定位01,剩余的位用来标识静态表中的Index
  • 第二个字节:首位用来表示是否使用Huffman编码压缩算法,剩余的7位标识value的长度,首位为1表示使用Huffman
  • 剩余的位数:value经过Huffman编码的数据

静态字典表和Huffman编码点击此处

动态表编码

静态表只包含61组数据,不在静态表的头部字符串就需要自行构建动态表,动态表的index从62开始。

使用动态表的前提是必须在同一个连接上,重复传输完全相同的HTTP头部。

动态表编码的缺点

随着HTTP2连接上发送的报文越来越多,动态表里面的数据也会越来越多,会吃掉越来越多的服务器内存资源,因此一般web服务器都会有参数用于限制一个连接上能够传输的请求数量,避免动态表无限增大,请求数量达到限制后会关闭HTTP2连接来释放内存。

HTTP2的头部压缩是通过「静态表 + 动态表 + Huffman编码」一起来实现。

二进制帧

HTTP2相比于HTTP1.1使用了二进制进行数据传输,提高了HTTP的传输效率,同时也方便了使用位运算对HTTP数据进行解析。

HTTP2把报文整体划分为两个帧,分别是Headers Frame和DATA Frame。

HTTP2帧结构

HTT2帧结构大体划分为两部分:

  • 9个字节的帧头
  • 帧数据

HTTP2的帧头主要由以下几部分:

  • Length:帧数据的长度
  • Type:帧类型
  • Flag:标志位,用于携带简单的控制信息
  • R:保留位
  • Stream Identifier:流标识符,用来标识该帧属于哪个Stream,接收方可以根据流标识符从乱序的帧中找到找到具有相同Stream ID的帧,然后进行组装
  • Frame Payload:帧数据,存放的是HTTP头部和包体

HTTP2帧类型

HTTP2的帧类型大体分为两种:

  • 数据帧
  • 控制帧

帧类型

类型编码

用途

数据帧 DATA

0x0

传输HTTP包体

数据帧 HEADERS

0x1

传输HTTP头部

数据帧 PRIORITY

0x2

指定Stream流的优先级

控制帧 RST_STREAM

0x3

终止Stream流

控制帧 SETTINGS

0x4

修改连接或者Stream流的配置

控制帧 PUSH_PROMISE

0x5

服务器推送资源时描述请求的帧

控制帧 PING

0x6

心跳检测,可以用于计算RTT

控制帧 GOAWAY

0x7

优雅的终止连接或者通知错误

控制帧 WINDOW_UPDATE

0x8

实现流量控制

控制帧 CONTINUATION

0x9

传递较大HTTP头部时持续的帧

并发传输

HTTP2的并发传输基于Stream实现的。

为什么需要并发传输?

HTTP1.1中同一个连接中,只有上一个请求和响应被处理后,才能继续处理下一个,也就是如果客户端发送的请求,服务端一直没有响应,客户端无法继续下一个请求,从而导致队头阻塞。

HTTP2如何实现并发传输?

HTTP2通过多个Stream复用一条TCP连接,达到并发效果。

  • 1个TCP连接包含一个或多个Stream
  • Stream里面可以包含1个或多个Message,Message对应HTTP1.1的请求或响应,由HTTP的头部和包体组成
  • Message里包含一个多个帧,帧是HTTP2的最小单位

不同Stream的帧是可以乱序发送的,接收方通过帧上的StreamId来区分该帧是由哪个Stream发送。

Stream ID的生成规则

客户端和服务器都可以建立Stream,客户端建立的Stream必须是奇数号,服务器建立的Stream必须是偶数号。

同一个连接中的Stream ID不能复用,必须严格顺序递增,如果StreamID消耗完,会发送一个GOAWAY控制帧关闭TCP连接。

HTTP2并发传输的优点

HTTP2在实现并发时,下层的TCP连接都是同一个,因此避免了TCP握手、慢启动以及TLS的握手过程,减少了耗时。

服务器主动推送资源

如何实现推送

服务器在主动推送资源时,会通过PUSH_PROMISE控制帧传输HTTP头部,并通过帧中的Promise Stream Id字段告知客户端接下来会在哪个Stream中发送包体。

HTTP2的队头阻塞问题

HTTP2虽然在应用层解决了队头阻塞问题,但由于下层还是使用一个TCP连接,所以HTTP2的队头阻塞问题存在于传输层。

HTTP2是基于TCP协议来传输数据的,TCP是字节流协议,TCP层必须保证收到的字节数据是完整且连续的,这样内核才会将缓冲区里的数据返回给HTTP应用,那么当前字节数据没有到达时,后收到的字节数据只能存放在内核缓冲区里,只有等到当前字节数据到达时,HTTP2应用层才能从内核中拿到数据,因此假设传输层不稳定,也会导致响应变慢队头阻塞。