Java 面试题:HTTP版本演变--xunznux

文章目录

HTTP版本演变

目前为止,HTTP 常见的版本有 HTTP/1.1,HTTP/2.0,HTTP/3.0,不同版本的 HTTP 特性是不一样的。

HTTP/0.9

HTTP/0.9 是最早的HTTP版本,在1991年就已经发布,只支持GET方法,也没有请求头,服务器只能返回 HTML格式的内容。

HTTP/1.0

HTTP/1.0 是HTTTP 协议的第一个正式版本,主要具有以下特性:

  • 引入了请求头和响应头,支持多种请求方法和状态码
  • 不支持持久连接,每次请求都需要建立新的连接

HTTP/1.1

新引入:

  • 引入长连接:只要客户端和服务器任意⼀端没有明确提出断开连接,则保持TCP连接状态。
  • 管道网络传输,管道化技术:同一个 TCP 连接里面,客户端可以发起多个请求,只要第一个请求发出去了,不必等其回来,就可以发第二个请求出去。
  • 管道解决了请求的队头阻塞问题,但是没有解决响应的队头阻塞。

问题:

  • 头部冗余:每个请求和响应都需要带有一定的头部信息,每次互相发送相同的首部造成的浪费较多;
  • 服务器是按请求的顺序响应的,如果服务器响应慢,会招致客户端一直请求不到数据,也就是响应队头阻塞;
  • 没有请求优先级控制;
  • 请求只能从客户端开始,服务器只能被动响应。

长连接是什么:

持久连接,只要客户端和服务器任意⼀端没有明确提出断开连接,则保持TCP连接状态。

管道网络传输:

在同一个 TCP 连接里面,客户端可以发起多个请求,只要第一个请求发出去了,不必等其回来,就可以发第二个请求出去,可以减少整体的响应时间。

客户端需要请求两个资源。以前的做法是,在同一个 TCP 连接里面,先发送 A请求,然后等待服务器做出回应收到后再发出 B 请求。那么,管道机制则是允许浏览器同时发出 A请求和 B 请求。

但是服务器必须按照接收请求的顺序发送对这些管道化请求的响应,如果服务端在处理 A 请求时耗时比较长,那么后续的请求的处理都会被阻塞住,这称为「队头堵塞」。所以,HTTP/1.1 管道解决了请求的队头阻塞,但是没有解决响应的队头阻塞。

HTTP/1.1 管道化技术不是默认开启,而且浏览器基本都没有支持。

队头阻塞是什么?

当顺序发送的请求序列中的一个请求因为某种原因被阻塞时,在后面排队的所有请求也一同被阻塞了,会招致客户端一直请求不到数据,这也就是「队头阻塞」。

http队首阻塞:简单理解就是需要排队,队首的事情没有处理完的时候,后面的人都要等着。

队头阻塞"与短连接和长连接无关,而是由 HTTP 基本的"请求 - 应答"机制所导致的。因为 HTTP 规定报文必须是"一发一收",这就形成了一个先进先出的"串行"队列。

而http的队首阻塞,在管道化和非管道化下,表现是不同的。

http1.0的队首阻塞 ( 非管道化下 ) :对于同一个tcp连接,所有的http1.0请求放入队列中,只有前一个请求的响应收到了,然后才能发送下一个请求,由下图可以看到,如果前一个请求卡着了,那么队列中后续的http就会阻塞。可见,http1.0的队首组阻塞在客户端。

http1.1的队首阻塞 ( 管道化下 ):对于同一个tcp连接,开启管道化后,http1.1允许一次发送多个http1.1请求,也就是说,不必等前一个响应收到,就可以发送下一个请求,这样就解决了http1.0的客户端的队首阻塞。但是,http1.1规定,服务器端的响应的发送要根据请求被接收的顺序排队,也就是说,先接收到的请求需要先响应回来。这样造成的问题是,如果最先收到的请求的处理时间长的话,响应生成也慢,就会阻塞已经生成了的响应的发送。也会造成队首阻塞,响应的阻塞。可见,应用管道化技术后,http1.1的队首阻塞发生在服务器端。

因此说,HTTP/1.1 管道解决了请求的队头阻塞,但是没有解决响应的队头阻塞。

解决http队头阻塞的方法:

1、并发TCP连接(浏览器一个域名采用6-8个TCP连接,并发HTTP请求),可以理解成增加了任务队列,不会导致一个任务阻塞了该任务队列的其他任务。

2、域名分片(多个域名,可以建立更多的TCP连接,从而提高HTTP请求的并发)可以在一个域名下分出多个二级域名出来,而它们最终指向的还是同一个服务器,这样子的话就可以并发处理的任务队列更多,也更好的解决了队头阻塞的问题。

3、HTTP2方式(多路复用特性)

HTTP1.1常见性能问题

  • 延迟难以下降,虽然现在网络的「带宽」相比以前变多了,但是延迟降到一定幅度后,就很难再下降了,说白了就是到达了延迟的下限;
  • 并发连接有限,谷歌浏览器最大并发连接数是6个,而且每一个连接都要经过 TCP 和 TLS握手耗时,以及TCP 慢启动过程给流量带来的影响;
  • 队头阻塞问题,同一连接只能在完成一个 HTTP 事务(请求和响应)后,才能处理下一个事务;HTTP 头部巨大且重复,由于 HTTP 协议是无状态的,每一个请求都得携带 HTTP 头部,特别是对于有携带cookie 的头部,而 cookie 的大小通常很大;
  • 不支持服务器推送消息,因此当客户端需要获取通知时,只能通过定时器不断地拉取消息,这无疑浪费大量了带宽和服务器资源。

为解决HTTP1.1性能问题而提出的常见优化手段

  • 将多张小图合并成一张大图供浏览器 JavaScript来切割使用,这样可以将多个请求合并成一个请求,但是带来了新的问题,当某张小图片更新了,那么需要重新请求大图片,浪费了大量的网络带宽;
  • 将图片的二进制数据通过 base64 编码后,把编码数据嵌入到 HTML或 CSS 文件中,以此来减少网络请求次数;
  • 将多个体积较小的 JavaScript 文件使用 webpack 等工具打包成一个体积更大的 JavaScript 文件,以一个请求替代了很多个请求,但是带来的问题,当某个 js 文件变化了,需要重新请求同一个包里的所有 js 文件;
  • 将同一个页面的资源分散到不同域名,提升并发连接上限,因为浏览器通常对同一域名的 HTTP 连接最大只能是6个。
    总的来说:
  • 将多个小资源合并为一个大资源,从而将多个请求合并为一个请求,减少请求次数,带来的问题是其中一个资源变化却需要更新所有资源,占用了带宽;
  • 将同一个页面的资源分散到不同的域名,提升并发连接上限,因为浏览器对同一域名的 HTTP 连接最大只能是6个。

HTTP/2

HTTP/2 协议是基于 HTTPS 的, 所以HTTP/2的安全性也是有保障的。

HTTP2协议还包括:流控制、流状态、依赖关系等。

1、兼容HTTP1.1

  1. 第一点,HTTP/2 没有在 URI里引入新的协议名,仍然用「http://」表示明文协议,用「https://」表示加密协议,于是只需要浏览器和服务器在背后自动升级协议,这样可以让用户意识不到协议的升级,很好的实现了协议的平滑升级。
  2. 第二点,只在应用层做了改变,还是基于 TCP 协议传输,应用层方面为了保持功能上的兼容,HTTP/2 把 HTTP 分解成了「语义」和「语法」两个部分,「语义」层不做改动,与 HTTP/1.1 完全一致,比如请求方法、状态码、头字段等规则保留不变。但是,HTTP/2 在「语法」层面做了很多改造,基本改变了 HTTP 报文的传输格式。

2、头部压缩

HTTP/2 使用 HPACK 压缩算法对请求和响应头部进行压缩,减少了传输的头部数据量,降低了延迟。

HPACK算法包含三个组成部分:

(i)静态字典

(ii)动态字典

(iii)Huffman编码(压缩算法)

客户端和服务器都会建立和维护【字典】,用长度较小的索引号表示重复的字符串,再用 Huffman 编码压缩数据。

为什么要压缩头部?

  1. 含很多固定的字段,比如Cookie、User Agent、Accept等,这些字段加起来也高达几百字节甚至上千字节,所以有必要压缩;
  2. 大量的请求和响应的报文里有很多字段值都是重复的,这样会使得大量带宽被这些冗余的数据占用了,所以有必要避免重复性;
  3. 字段是 ASCI 编码的,虽然易于人类观察,但效率低,所以有必要改成二进制编码。

3、二进制帧

二进制帧: HTTP/2 将数据分割成二进制帧进行传输,分为头信息帧(Headers Frame)和数据帧(Data Frame),增加了数据传输的效率。

HTTP2更厉害的地方在于: 将HTTP1的文本格式改成二进制格式传输数据,极大提高了HTTP传输效率,而且二进

制数据使用位运算能高效解析。

暂时无法在飞书文档外展示此内容

4、Stream并发传输(多路复用)

引出了 Stream 概念,多个 Stream 复用在一条 TCP 连接,针对不同的 HTTP 请求用独一无二的Stream ID 来区分,接收端可以通过 Stream lD 有序组装成 HTTP 消息,不同 Stream 的帧是可以乱序发送的,因此可以并发不同的 Stream ,也就是 HTTP/2 可以并行交错地发送请求和响应。

流标识符:用来标识该Frame属于哪个Stream。接收方可以根据这个信息从乱序的帧里找到相同Stream ID的帧,从而有序组装信息。

  • HTTP1.1 基于请求-响应模型。同一个连接中,HTTP完成一个事务(请求与响应),才能处理下一个事务。即:再发出请求等待响应的过程中是没办法做其他事情的,会造成【队头阻塞】问题。
  • HTTP2通过Stream这个设计(多个Stream复用一条TCP连接,达到并发的效果),解决了【队头阻塞】的问题,提高了HTTP传输的吞吐量。
  • 1个 TCP 连接包含一个或者多个 Stream,Stream 是 HTTP/2 并发的关键技术;
  • Stream 里可以包含1个或多个 Message,Message 对应 HTTP/1 中的请求或响应,由 HTTP 头部和包体构成,
  • Message 里包含一条或者多个 Frame,Frame是 HTTP/2 最小单位,以二进制压缩格式存放 HTTP/1 中的内容(头部和包体)。包括头信息帧(Headers Frame)和数据帧(Data Frame)。
  • HTTP消息可以由多个Frame构成。
  • 一个Frame可以由多个TCP报文构成。

在HTTP2连接上,不同Stream的帧可以乱序发送(因此可以并发不同的Stream),接收端可以通过Stream ID 有序组装HTTP消息。

  • 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 的优先级来实现,以此提高用户体验。

5、服务器主动推送(存在TCP层的队头阻塞)

在 HTTP/2 中,服务器可以对客户端的一个请求发送多个响应,即服务器可以额外的向客户端推送资源,而无需客户端明确的请求。

但是 HTTP/2 仍然存在着队头阻塞的问题,只不过问题是在传输层。

HTTP/2 是基于 TCP 协议来传输数据的,TCP 是字节流协议,TCP 层必须保证收到的字节数据是完整且连续的,这样内核才会将缓冲区里的数据返回给 HTTP 应用,那么当「前1个字节数据」没有到达时,后收到的字节数据只能存放在内核缓冲区里,只有等到这1个字节数据到达时,HTTP/2 应用层才能从内核中拿到数据,这就是 HTTP/2队头阻塞问题。

HTTP1.1不支持服务器主动推送资源给客户端,都是由客户端向服务器发起请求后,才能获取到服务器响应的资

源。在HTTP2中,客户端在访问HTML时,服务器可以直接主动推送CSS⽂件,减少了消息传递的次数。

客户端发起的请求,必须使用的是奇数号 Stream,服务器主动的推送,使用的是偶数号 Stream。服务器在推送资源时,会通过 PUSH_PROMISE 帧传输 HTTP 头部,并通过帧中的 Promised Stream ID 字段告知客户端,接下来会在哪个偶数号 Stream 中发送包体。

暂时无法在飞书文档外展示此内容

HTTP/3(解决TCP队头阻塞)

HTTP/2 队头阻塞的问题是因为 TCP,所以 HTTP/3 把 HTTP 下层的 TCP 协议改成了 UDP!

HTTP/3 基于 QUIC 协议,具有以下特点:

  • 零 RTT 连接建立: QUIC(Quick UDP Internet Connections)允许在首次连接时进行零往返时间(Zero Round Trip Time)连接建立,从而减少了连接延迟,加快了页面加载速度。
  • 无队头阻塞: QUIC 使用 UDP 协议来传输数据。一个连接上的多个stream之间没有依赖,如果一个stream丢了一个UDP包,不会影响后面的stream,不存在TCP 队头阻塞。
  • 连接迁移: OUIC允许在网络切换(如从 Wi-Fi到移动网络)时,将连接迁移到新的 IP 地址,从而减少连接的中断时间。
  • 向前纠错机制: 每个数据包除了它本身的内容之外,还包括了部分其他数据包的数据,因此少量的丢包可以通过其他包的冗余数据直接组装而无需重传。向前纠错牺牲了每个数据包可以发送数据的上限,但是减少了因为丢包导致的数据重传。

HTTP/2的缺点

HTTP2 协议是基于TCP实现的,所以存在三个缺陷:

  1. 队头阻塞:TCP 是字节流协议,TCP 层必须保证收到的字节数据是完整且有序的,如果序列号较低的 TCP 段在网络传输中丢失了,即使序列号较高的 TCP 段已经被接收了,应用层也无法从内核中读取到这部分数据,从HTTP 视角看,就是请求被阻塞了。
  2. TCP与TLS的握手时延迟:发出HTTP请求时,需要经过TCP三次握手和TLS四次握手(TLS 1.2是4次,TLS 1.3是三次,开启Fast Open后可以和TCP同时进行),共计3 RTT的时延才能发出请求数据。
  3. 网络迁移需要重新连接:一个TCP连接由【源IP地址,源端口,目标IP地址,目标端口】确定。若IP地址或端口发生变暖,这需要重新进行连接。这不利于移动设备切换网络的场景。要解决该问题,就要修改传输层协议。在HTTP3中传输层协议修改为了 UDP。

QUIC协议的特点(将UDP变成可靠的)

UDP是一个简单的、不可靠的传输协议,而且UDP包之间是无序的,也没有依赖关系。UDP也不需要连接。

HTTP3基于UDP协议在应用层实现了 QUIC 协议,它有类似TCP的连接管理、拥塞窗口、流量控制的网络特性,相当于将不可靠的UDP协议变成可靠的了,无需担心数据包丢包的问题。

无队头阻塞

  • HTTP2:只要某个流中的数据包丢失了,其他流也会因此受影响。
  • HTTP3:流与流(Stream)之间不影响

更快的连接建立

对于 HTTP/1 和 HTTP/2 协议,TCP 和 TLS 是分层的,分别属于内核实现的传输层、openssl库实现的表示层,因此它们难以合并在一起,需要分批次来握手,先 TCP 握手,再 TLS 握手。

HTTP/3 在传输数据前虽然需要 QUIC协议握手,这个握手过程只需要1RTT,握手的目的是为确认双方的「连接ID」,连接迁移就是基于连接 ID 实现的。

但是 HTTP/3 的 QUIC 协议并不是与 TLS 分层,而是QUIC 内部包含了 TLS,它在自己的帧会携带 TLS 里的"记录"、再加上 QUIC 使用的是 TLS1.3,因此仅需1个 RTT 就可以「同时」完成建立连接与密钥协商,甚至在第二次连接的时候,应用数据包可以和 QUIC握手信息(连接信息+TLS 信息)一起发送,达到 0-RTT 的效果。

为什么QUIC只需要一个RTT就可以完成建立连接与密钥协商

首先,TCP的三次握手需要 1个RTT 来完成。

三次握手过程:

  1. 客户端发送SYN(Synchronize)报文:客户端向服务器发送一个SYN报文,用于请求建立连接。这是握手的第一步。
  2. 服务器响应SYN-ACK:服务器收到SYN报文后,回应一个SYN-ACK报文,表示同意建立连接,并确认收到了客户端的SYN。这是握手的第二步。
  3. 客户端发送ACK:客户端收到SYN-ACK后,发送一个ACK(Acknowledgment)报文,确认收到了服务器的响应。这是握手的第三步,完成了连接的建立。
    时延解释:
  • 发送SYN并接收到SYN-ACK:这一步耗费1个RTT(客户端到服务器再返回客户端)。
  • 发送ACK:这一步通常不需要额外的RTT,因为在发送ACK后,客户端和服务器都已经可以开始发送数据了。
    因此,TCP三次握手只需要 1个RTT 来完成连接的建立。

总结:而QUIC使用的是TLS 1.3 只需要三次握手,与TCP相同,因此如果它与TCP一起传输数据,可以看作只需要进行TCP连接,也就是只需要 1个RTT。

连接迁移

基于 TCP 传输协议的 HTTP 协议,由于是通过四元组(源 IP、源端口、目的IP、目的端口)确定一条 TCP 连接,那么当移动设备的网络从 4G 切换到 WIFI 时,意味着 IP 地址变化了,那么就必须要断开连接,然后重新建立连接,而建立连接的过程包含 TCP 三次握手和 TLS 四次握手的时延,以及 TCP 慢启动的减速过程,给用户的感觉就是网络突然卡顿了一下,因此连接的迁移成本是很高的。

而 QUIC 协议没有用四元组的方式来"绑定"连接,而是通过连接 ID来标记通信的两个端点,客户端和服务器可以各自选择一组 ID 来标记自己,因此即使移动设备的网络变化后,导致IP 地址变化了,只要仍保有上下文信息(比如连接 ID、TLS 密钥等),就可以"无缝"地复用原连接,消除重连的成本,没有丝毫卡顿感,达到了连接迁移的功能。

HTTP/3 同 HTTP/2 一样采用二进制帧的结构,不同的地方在于 HTTP/2 的二进制帧里需要定义 Stream,而HTTP/3 自身不需要再定义 stream,直接使用 QUIC 里的 Stream,于是 HTTP/3 的帧的结构也变简单了。

相关推荐
Riu_Peter2 分钟前
【技术】Maven 配置 settings.xml 轮询下载
xml·java·maven
Rust语言中文社区8 分钟前
【Rust日报】用 Rust 重写的 Turso 是一个更好的 SQLite 吗?
开发语言·数据库·后端·rust·sqlite
蓝田生玉12317 分钟前
qwen2.5论文阅读笔记
论文阅读·笔记
十六年开源服务商34 分钟前
2026年WordPress网站地图完整指南
java·前端·javascript
Edward111111111 小时前
3月17枚举
java·开发语言
Emberone1 小时前
从C到C++:一脚踹开面向对象的大门
开发语言·c++
凡。。。2961 小时前
阿里云产品说明
java
JosieBook1 小时前
【C#】C# 所有关键字总结
开发语言·算法·c#
F1FJJ1 小时前
只是想查个数据,不想装 phpMyAdmin
数据库·网络协议·容器·开源软件