设为首页 加入收藏

TOP

前后端开发必会的 HTTP 协议“十全大补丸”(万字长文)(四)
2023-07-25 21:39:19 】 浏览:75
Tags:后端开 HTTP 协议 全大补
处理都会被阻塞住,会导致客户端迟迟收不到数据,这称为「队头堵塞」。

实际上,虽然管道机制的想法很好,但实现却非常困难,因而很多浏览器根本不支持它。一般为了提升性能,采用并行多个 TCP 连接的形式来实现请求的同时发送。

③ 缓存控制

前文已经提到过,HTTP/1.1 在 HTTP/1.0 基础之上,增加了一些请求响应头,以更好的实现对缓存的控制。比如

  • 新增 Cache-Control 代替原先的 Expires
  • 新增 If-None-MatchEtag 代替原先的 If-Modified-SinceLast-Modified

④ 断点续传

利? HTTP 消息头使?分块传输编码,将实体主体分块传输。

(2)HTTP/2 相比 HTTP/1.1 性能上的改进

HTTP/2 协议本身是基于 HTTPS 的,因此更加安全,其相对于 HTTP/1.1 有以下改进。

① 头部压缩

HTTP/1.1 中的请求头携带大量信息,而且每次都要重复发送,即使是同样的内容,每次请求都需要附带,这会造成性能的损耗。HTTP/2 进行了优化,引入了头信息压缩机制

客户端和服务器同时维护一张头信息表,高频出现的字段会存入这个表,生成一个索引号。发送报文时直接使用索引号替代字段。另外,索引表中不存在的字段使用哈夫曼编码压缩

同时,多个请求中,如果请求头相同,则后续请求只需要发送差异的部分,重复的部分无需再发送

② 二进制帧

HTTP/1.1 的报文为纯文本格式,而 HTTP/2 的报文全面采用二进制格式,并将原始的报文拆分为头信息帧(Headers Frame)和数据帧(Data Frame)。采用二进制格式有利于提升数据传输效率。

③ 多路复用

在 HTTP/2 中定义了流(Stream)的概念,它是二进制帧的双向传输序列,一个数据流对应着一个完整的请求-响应过程,在同一个请求响应过程中,往返的帧会分配一个唯一的流编号(Stream ID)。

在流的支持下,HTTP/2 可以在一个 TCP 连接中传输多个请求或响应,而不用按照顺序一一对应(即实现多路复用),因为它们属于不同的流,所发送的帧头部都会携带 Stream ID,可以通过此 Stream ID 有效区分不同的请求-响应。

因而 HTTP/2 解决了 HTTP/1.1 的『队头阻塞』问题,多个请求 - 响应之间没有了顺序关系,不需要排队等待,降低了延迟,大幅度提高了连接的利用率。

举个栗子:

在一个 TCP 连接里面,服务器同时收到了 A 请求和 B 请求,于是先回应 A 请求,结果发现处理过程非常耗时,于是就发送 A 请求已经处理好的部分,接着回应 B 请求,完成后,再发送 A 请求剩下的部分。

④ 服务端推送

在 HTTP/1.1 中,只能客户端发起请求,服务器对请求进行响应。

而在 HTTP/2 中,服务端可以主动给客户端推送必要的资源,以减少请求延迟时间。

比如当客户端向服务器请求一个 HTML 文件后,服务器除了将此 HTML 文件响应给客户端外,还可以提前主动将此 HTML 中所依赖的 JSCSS 文件推送给客户端,这样客户端在解析 HTML 时,无需耗费额外的请求去得到相应的 JSCSS 文件。

(3)HTTP/3 相比 HTTP/2 性能上的改进

Google 公司为了解决 HTTP/2 存在的一些问题,提出了 QUIC 协议,而 HTTP-over-QUIC 就是 HTTP/3,其相对于 HTTP/2 有以下改进。

① 无队头阻塞

前面提到,HTTP/2 通过多路复用解决了 HTTP1.1 的『队头阻塞』问题,但其只是解决了 HTTP 这一层面的『队头阻塞』问题,底层仍然采用的 TCP 连接,HTTP/2 并没有解决 TCP 的『队头阻塞』问题。

TCP 是可靠的、面向字节流的协议。HTTP/2 的多个请求虽然可以跑在同一个 TCP 连接中,但如果出现丢包现象,TCP 就需要进行重传,这可能就会导致整个 TCP 连接上的所有流阻塞,直到丢的包重传成功,这就是 TCP 的『队头阻塞』问题。

为了解决此问题,HTTP/3 底层不再使用 TCP,而是采用 UDP!而 UDP 是无连接的,多个流互相独立,之间不再有依赖,因而即使某个流发生了丢包,只会对该流产生影响,并不会使得其他流阻塞!

这时候有的小伙伴可能会问了,HTTP/3 底层不采用 TCP,那怎么保证可靠传输呢?答案就是 HTTP/3 在应用层自己重新实现了可靠性机制。也就是说,HTTP/3 将原先 TCP 协议提供的部分功能上移至 QUIC,而且进行了改进。

② 优化重传机制

TCP 采用序号+确认号+超时重传机制来保证消息的可靠性,即如果某条消息超过一定时间还没有得到确认,则重新发送此消息。

由于网络拥堵情况不断变化,因而消息的超时时间并不是固定的,而是通过不断采样消息的往返时间不断调整的,但 TCP 超时采样存在不准确的问题

举个栗子:

客户端发送一个序号为 N 的包,然后超时了(可能丢了,也可能网络堵塞了),于是重新发送一个序号为 N 的包,之后服务器收到后返回一个确认号 ACK 为 N+1 的包。但此时客户端并无法判断这个确定包是对原始报文的确认还是重传报文的确认,那么此时往返时间应该如何计算呢?

  • 如果认为确认包是对原始报文的确认,则可能把时间算长了;
  • 如果认为确认包是对重传报文的确认,则可能包时间算长了。

因而 TCP 的重传超时时间计算不准确,如果计算偏大,则效率慢,很久才会重传,而如果计算偏小,则可能确认报文已经在路上了,但却重传了!

QUIC 是如何解决此问题呢?其定义了一个递增的序列号(不再叫 Seq,而是 Packet Number),每个序列号的包只发送一次,即使重传相同的包,其序列号也不一样

举个栗子:

客户端发送一个序号为 N 的包,然后超时了,于是重新发送一个相同的包,但序号不再是 N,而是 N+1;那么如果返回的确认包 ACK 为 N+1,就是对原始报文的响应,如果 ACK 为 N+2,就是对重传报文的响应,因而采样时间计算相对更加准确!

那此时怎么知道包 N 和包 N+1 是同一个包呢?QUIC 定义了一个 Offset 概念。发送的数据有个偏移量 Offset,可以通过 Offset 知道数据目前发送到了哪里,因而如果某个 Offset 的包没有收到确认,就重发。

③ 连接迁移

众所周知,一条 TCP 连接是由四元组标识的,分别是源 IP、源端口、目的 IP、目的端口。一旦其中一个元素发生了变化,就需要断开重连。

当手机信号不稳定或者在 WIFI 与移动网络切换时,都将会导致重连,而重连就意味着需要重新进行三次握手,将产生一定的时延,用户感到卡顿,体验不友好。

而 QUIC 不采用四元组的方式标识连接,而是以一个 64 位的随机数作为 ID 来标识,通过此连接 ID 标记通信的两端,之后即使网络发生变化,IP 或端口变了,但只要 ID 不变,则无需重连,只需要复用原先连接即可,时延低,减少了用户的卡顿感,实现连接迁移。

(4)总结

首页 上一页 1 2 3 4 下一页 尾页 4/4/4
】【打印繁体】【投稿】【收藏】 【推荐】【举报】【评论】 【关闭】 【返回顶部
上一篇spring xml配置中引用java配置不.. 下一篇每日算法之左旋转字符串

最新文章

热门文章

Hot 文章

Python

C 语言

C++基础

大数据基础

linux编程基础

C/C++面试题目