【图解】HTTP/1.1到HTTP/2.0的演变


HTTP/1.1

HTTP/1.1 相⽐ HTTP/1.0 性能上的改进:

  • 使⽤ TCP ⻓连接的⽅式改善了 HTTP/1.0 短连接造成的性能开销。

  • ⽀持管道(pipeline)⽹络传输,只要第⼀个请求发出去了,不必等其回来,就可以发第⼆个请求出去,可以减少整体的响应时间。

HTTP2

HTTP/1.1性能问题

  • 延迟难以下降,虽然现在⽹络的「带宽」相⽐以前变多了,但是延迟降到⼀定幅度后,就很难再下降了,说⽩了就是到达了延迟的下限;

  • 并发连接有限,⾕歌浏览器最⼤并发连接数是 6 个,⽽且每⼀个连接都要经过 TCP 和 TLS 握⼿耗时,以及TCP 慢启动过程给流ᰁ带来的影响;

  • 队头阻塞问题,同⼀连接只能在完成⼀个 HTTP 事务(请求和响应)后,才能处理下⼀个事务;

  • HTTP 头部巨⼤且重复,由于 HTTP 协议是⽆状态的,每⼀个请求都得携带 HTTP 头部,特别是对于有携带cookie 的头部,⽽ cookie 的⼤⼩通常很⼤;

  • 不⽀持服务器推送消息,因此当客户端需要获取通知时,只能通过定时器不断地拉取消息,这⽆疑浪费⼤了带宽和服务器资源。

尽管对 HTTP/1.1 协议的优化⼿段如此之多,但是效果还是不尽⼈意,因为这些⼿段都是对 HTTP/1.1 协议的“外部”做优化,⽽⼀些关键的地⽅是没办法优化的,比如请求响应模型、头部巨⼤且重复、并发连接耗时、服务器不能主动推送等,要改变这些必须重新设计HTTP协议,于是HTTP/2 就出来了!

HTTP2.0的优化:

1. 头部压缩

HTTP 协议的报⽂是由「Header + Body」构成的,对于 Body 部分,HTTP/1.1 协议可以使⽤头字段 「Content Encoding」指定 Body 的压缩⽅式,⽐如⽤ gzip 压缩,这样可以节约带宽,但报⽂中的另外⼀部分 Header,是没有针对它的优化⼿段。

HTTP/1.1 报⽂中 Header 部分存在的问题:

  • 含很多固定的字段,⽐如Cookie、User Agent、Accept 等,这些字段加起来也⾼达⼏百字节甚⾄上千字节,所以有必要压缩

  • ⼤量的请求和响应的报⽂⾥有很多字段值都是重复的,这样会使得⼤ᰁ带宽被这些冗余的数据占⽤了,所以必须要避免重复性

  • 字段是 ASCII 编码的,虽然易于⼈类观察,但效率低,所以有必要改成⼆进制编码

HTTP/2 对 Header 部分做了⼤改造,把以上的问题都解决了。

HTTP/2 没使⽤常⻅的 gzip 压缩⽅式来压缩头部,⽽是开发了 HPACK 算法,HPACK 算法主要包含三个组成部分:

  • 静态字典;

  • 动态字典;

  • Huffman 编码(压缩算法);

客户端和服务器两端都会建⽴和维护「字典」,⽤⻓度较⼩的索引号表示重复的字符串,再⽤ Huffman 编码压缩数据,可达到 50%~90% 的高压缩率

2. 二进制帧

HTTP/2 厉害的地⽅在于将 HTTP/1 的⽂本格式改成⼆进制格式传输数据,极⼤提⾼了 HTTP 传输效率,⽽且⼆进制数据使⽤位运算能⾼效解析。

你可以从下图看到,HTTP/1.1 的响应 和 HTTP/2 的区别:
在这里插入图片描述

HTTP/2 把响应报⽂划分成了两个帧(Frame),图中的 HEADERS(⾸部)和 DATA(消息负载) 是帧的类型,也就是说⼀条 HTTP 响应,划分成了两个帧来传输,并且采⽤⼆进制来编码。

HTTP/2 ⼆进制帧的结构如下图:

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-fRPIo2TY-1645449674274)(C:\Users\DB\AppData\Roaming\Typora\typora-user-images\image-20220221171934404.png)]

帧⻓度后⾯的⼀个字节是表示帧的类型,HTTP/2 总共定义了 10 种类型的帧,⼀般分为数据帧控制帧两类,如下表格:

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-St4nHmgm-1645449674275)(C:\Users\DB\AppData\Roaming\Typora\typora-user-images\image-20220221172030529.png)]

帧类型后⾯的⼀个字节是标志位,可以保存 8 个标志位,⽤于携带简单的控制信息,⽐如:

  • END_HEADERS 表示头数据结束标志,相当于 HTTP/1 ⾥头后的空⾏(“\r\n”);

  • END_STREAM 表示单⽅向数据发送结束,后续不会再有数据帧。

  • PRIORITY 表示流的优先级;

帧头的最后 4 个字节是流标识符(Stream ID),但最⾼位被保留不⽤,只有 31 位可以使⽤,因此流标识符的最⼤值是 2^31,⼤约是 21 亿,它的作⽤是⽤来标识该 Fream 属于哪个 Stream,接收⽅可以根据这个信息从乱序的帧⾥找到相同 Stream ID 的帧,从⽽有序组装信息。

最后⾯就是帧数据了,它存放的是通过 HPACK 算法压缩过的 HTTP 头部和包体。

3. 并发传输(多路复用)

HTTP/2 是可以在⼀个连接中并发多个请求或回应,⽽不⽤按照顺序⼀⼀对应

知道了 HTTP/2 的帧结构后,我们再来看看它是如何实现并发传输的。

我们都知道 HTTP/1.1 的实现是基于请求-响应模型的。同⼀个连接中,HTTP 完成⼀个事务(请求与响应),才能处理下⼀个事务,也就是说在发出请求等待响应的过程中,是没办法做其他事情的,如果响应迟迟不来,那么后续的请求是⽆法发送的,也造成了队头阻塞的问题。

⽽ HTTP/2 就很⽜逼了,通过 Stream 这个设计,多个 Stream 复⽤⼀条 TCP 连接,达到并发的效果,解决了HTTP/1.1 队头阻塞的问题,提⾼了 HTTP 传输的吞吐量。

为了理解 HTTP/2 的并发是怎样实现的,我们先来理解 HTTP/2 中的 Stream、Message、Frame 这 3 个概念。

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-mLWX4H01-1645449674276)(C:\Users\DB\AppData\Roaming\Typora\typora-user-images\image-20220221175332285.png)]

你可以从上图中看到:

  • 1 个 TCP 连接包含⼀个或者多个 Stream,Stream 是 HTTP/2 并发的关键技术;

  • Stream ⾥可以包含 1 个或多个 Message,Message 对应 HTTP/1 中的请求或响应,由 HTTP 头部和包体构成;

  • Message ⾥包含⼀条或者多个 Frame,Frame 是 HTTP/2 最⼩单位,以⼆进制压缩格式存放 HTTP/1 中的内容(头部和包体);

因此,我们可以得出 2 个结论:HTTP 消息可以由多个 Frame 构成,以及 1 个 Frame 可以由多个 TCP 报⽂构成。

在 HTTP/2 连接上,不同 Stream 的帧是可以乱序发送的(因此可以并发不同的 Stream ,因为每个帧的头部会携带 Stream ID 信息,所以接收端可以通过 Stream ID 有序组装成 HTTP 消息,⽽同⼀Stream内部的帧必须是严格有序的

客户端和服务器双⽅都可以建⽴ Stream, Stream ID 也是有区别的,客户端建⽴的 Stream 必须是奇数号,⽽服务器建⽴的 Stream 必须是偶数号。

HTTP/2 通过 Stream 实现的并发,⽐ HTTP/1.1 通过 TCP 连接实现并发要⽜逼的多,因为当HTTP/2实现100 个并发Stream 时,只需要建⽴⼀次TCP连接,⽽HTTP/1.1需要建⽴100个TCP连接,每个TCP连接都要经过TCP握⼿、慢启动以及TLS握⼿过程,这些都是很耗时的。

HTTP/2 还可以对每个 Stream 设置不同优先级,帧头中的「标志位」可以设置优先级,⽐如客户端访问HTML/CSS 和图⽚资源时,希望服务器先传递 HTML/CSS,再传图⽚,那么就可以通过设置 Stream 的优先级来实现,以此提⾼⽤户体验。

4. 服务器主动推送资源

HTTP/1.1 不⽀持服务器主动推送资源给客户端,都是由客户端向服务器发起请求后,才能获取到服务器响应的资源。

⽐如,客户端通过 HTTP/1.1 请求从服务器那获取到了 HTML ⽂件,⽽ HTML 可能还需要依赖 CSS 来渲染⻚⾯,这时客户端还要再发起获取 CSS ⽂件的请求,需要两次消息往返,如下图左边部分:

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-CxYXM2Ai-1645449674277)(C:\Users\DB\AppData\Roaming\Typora\typora-user-images\image-20220221180129606.png)]

如上图右边部分,在 HTTP/2 中,客户端在访问 HTML 时,服务器可以直接主动推送 CSS ⽂件,减少了消息传递的次数。

猜你喜欢

转载自blog.csdn.net/weixin_60297362/article/details/123056292