摘要
兼容HTTP1.1
HTTP2的优点我们后面会一一列出,但是一个新的东西的升级必须要做到向前兼容才能快速推广,因为只有这样才能减少对用户的影响。
HTTP2对HTTP1.1的兼容体现在哪些方面
头部压缩
HTTP1.1只能对请求体无法压缩,但HTTP2除了对请求体进行压缩还可以对Heder进行压缩。
HTTP2 Header的压缩方式
HPACK算法,HPACK算法主要包含三个部分:
客户端和服务端会建立维护好静态字典和动态字典,用长度较小的索引号表示重复的字符串,另外一些不固定的变化的value需要通过Huffman进行编码。
静态字典
HTTP2为Head中高频出现的字符串和字段建立了一张静态表,静态表中一共维护了61项,主要包含三项内容:
为什么有些Header Value不存在?
有些Head Value不是固定的,这些value需要经过Huffman压缩后在进行发送。
Head的格式
如果Head字段属于静态字典,那么Head的固定格式如下:
静态字典表和Huffman编码点击此处
动态表编码
静态表只包含61组数据,不在静态表的头部字符串就需要自行构建动态表,动态表的index从62开始。
使用动态表的前提是必须在同一个连接上,重复传输完全相同的HTTP头部。
动态表编码的缺点
随着HTTP2连接上发送的报文越来越多,动态表里面的数据也会越来越多,会吃掉越来越多的服务器内存资源,因此一般web服务器都会有参数用于限制一个连接上能够传输的请求数量,避免动态表无限增大,请求数量达到限制后会关闭HTTP2连接来释放内存。
HTTP2的头部压缩是通过「静态表 + 动态表 + Huffman编码」一起来实现。
二进制帧
HTTP2相比于HTTP1.1使用了二进制进行数据传输,提高了HTTP的传输效率,同时也方便了使用位运算对HTTP数据进行解析。
HTTP2把报文整体划分为两个帧,分别是Headers Frame和DATA Frame。
HTTP2帧结构
HTT2帧结构大体划分为两部分:
HTTP2的帧头主要由以下几部分:
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连接,达到并发效果。
不同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应用层才能从内核中拿到数据,因此假设传输层不稳定,也会导致响应变慢队头阻塞。