HTTP 1.0, 2.0 和 3.0 有什么区别?
HTTP/1.0 就像是“一问一答”的电话,每次打电话(请求)都得先拨号(建立连接),说完一句话(发送数据)就挂断(关闭连接),再打下一通电话。效率比较低。
HTTP/2.0 就像是“多路复用”的电话会议,一次拨号(建立连接)后,大家可以在同一个会议室里同时说多句话(多路复用),而且还可以压缩语言(头部压缩),甚至会议主持人(服务器)可以提前把大家可能需要的文件准备好(服务器推送)。这样效率就高多了。
为什么需要 HTTP?
在理解 HTTP/1.0 和 HTTP/2.0 的区别之前,我们得先明白 HTTP 协议本身是干嘛的。
想象一下,互联网就像一个巨大的图书馆。你(客户端)想从图书馆里借一本书(资源),图书馆管理员(服务器)需要知道你要哪本书,然后把书给你。HTTP(HyperText Transfer Protocol,超文本传输协议)就是你和图书馆管理员之间交流的“语言”或“规矩”。它规定了你如何提出请求,管理员如何回应,以及数据如何传输。
核心目标: 让客户端和服务器能够高效、可靠地交换信息(主要是网页、图片、视频等资源)。
从 1.0 到 2.0
HTTP/1.0:初期的“一问一答”模式
HTTP/1.0 是互联网早期设计的协议,它非常简单直接,就像我们上面说的“一问一答”的电话。
核心思想: 每次请求-响应都建立一个新的 TCP 连接,完成后立即关闭。
- 用户需求: 我要一个网页。
- 网页构成: 一个网页通常不只包含 HTML 文本,还有图片、CSS 文件、JavaScript 文件等等。
- 1.0 的做法:
- 客户端请求 HTML 文件。
- 服务器响应 HTML 文件。
- 连接关闭。
- 客户端解析 HTML,发现还需要图片 A。
- 客户端再次建立 TCP 连接,请求图片 A。
- 服务器响应图片 A。
- 连接关闭。
- …以此类推,直到所有资源都加载完毕。
这种模式有什么缺点?
- 连接建立/关闭开销大: 每次建立 TCP 连接都需要“三次握手”,关闭需要“四次挥手”,这就像每次打电话都要先拨号、等待接通、再挂断,非常耗时。
- 队头阻塞(Head-of-Line Blocking): 即使你有很多请求要发,也必须等前一个请求的响应完全回来,才能发送下一个请求。这就像你在排队买票,前面的人没买完,你就不能买。
- 带宽利用率低: 连接频繁建立和关闭,导致网络带宽无法持续高效利用。
流程图:HTTP/1.0 请求流程
#mermaid-svg-YZOAqjFveSB8HD2p {font-family:\"trebuchet ms\",verdana,arial,sans-serif;font-size:16px;fill:#333;}#mermaid-svg-YZOAqjFveSB8HD2p .error-icon{fill:#552222;}#mermaid-svg-YZOAqjFveSB8HD2p .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-YZOAqjFveSB8HD2p .edge-thickness-normal{stroke-width:2px;}#mermaid-svg-YZOAqjFveSB8HD2p .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-YZOAqjFveSB8HD2p .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-YZOAqjFveSB8HD2p .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-YZOAqjFveSB8HD2p .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-YZOAqjFveSB8HD2p .marker{fill:#333333;stroke:#333333;}#mermaid-svg-YZOAqjFveSB8HD2p .marker.cross{stroke:#333333;}#mermaid-svg-YZOAqjFveSB8HD2p svg{font-family:\"trebuchet ms\",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-YZOAqjFveSB8HD2p .label{font-family:\"trebuchet ms\",verdana,arial,sans-serif;color:#333;}#mermaid-svg-YZOAqjFveSB8HD2p .cluster-label text{fill:#333;}#mermaid-svg-YZOAqjFveSB8HD2p .cluster-label span{color:#333;}#mermaid-svg-YZOAqjFveSB8HD2p .label text,#mermaid-svg-YZOAqjFveSB8HD2p span{fill:#333;color:#333;}#mermaid-svg-YZOAqjFveSB8HD2p .node rect,#mermaid-svg-YZOAqjFveSB8HD2p .node circle,#mermaid-svg-YZOAqjFveSB8HD2p .node ellipse,#mermaid-svg-YZOAqjFveSB8HD2p .node polygon,#mermaid-svg-YZOAqjFveSB8HD2p .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-YZOAqjFveSB8HD2p .node .label{text-align:center;}#mermaid-svg-YZOAqjFveSB8HD2p .node.clickable{cursor:pointer;}#mermaid-svg-YZOAqjFveSB8HD2p .arrowheadPath{fill:#333333;}#mermaid-svg-YZOAqjFveSB8HD2p .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-YZOAqjFveSB8HD2p .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-YZOAqjFveSB8HD2p .edgeLabel{background-color:#e8e8e8;text-align:center;}#mermaid-svg-YZOAqjFveSB8HD2p .edgeLabel rect{opacity:0.5;background-color:#e8e8e8;fill:#e8e8e8;}#mermaid-svg-YZOAqjFveSB8HD2p .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-YZOAqjFveSB8HD2p .cluster text{fill:#333;}#mermaid-svg-YZOAqjFveSB8HD2p .cluster span{color:#333;}#mermaid-svg-YZOAqjFveSB8HD2p div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:\"trebuchet ms\",verdana,arial,sans-serif;font-size:12px;background:hsl(80, 100%, 96.2745098039%);border:1px solid #aaaa33;border-radius:2px;pointer-events:none;z-index:100;}#mermaid-svg-YZOAqjFveSB8HD2p :root{--mermaid-font-family:\"trebuchet ms\",verdana,arial,sans-serif;}客户端请求 HTML建立 TCP 连接发送 HTTP 请求 (HTML)服务器处理发送 HTTP 响应 (HTML)关闭 TCP 连接客户端解析 HTML
发现需要图片1建立 TCP 连接发送 HTTP 请求 (图片1)服务器处理发送 HTTP 响应 (图片1)关闭 TCP 连接重复上述过程
直到所有资源加载完毕
HTTP/1.1:小修小补,引入持久连接
HTTP/1.1 在 1.0 的基础上做了一些改进,最核心的就是引入了持久连接(Persistent Connections),也叫 Keep-Alive。
默认情况下,一个 TCP 连接在发送完一个请求-响应后不会立即关闭,而是保持一段时间,允许在这个连接上发送后续的请求。
- 1.0 的痛点: 频繁建立/关闭连接。
- 如何优化? 既然一个网页需要多个资源,那能不能只建立一次连接,然后在这个连接上把所有资源都请求完再关闭呢?
- 1.1 的做法:
- 客户端请求 HTML 文件。
- 建立 TCP 连接。
- 发送 HTTP 请求 (HTML)。
- 服务器响应 HTML。
- 连接保持开放。
- 客户端解析 HTML,发现需要图片 A。
- 在同一个 TCP 连接上,发送 HTTP 请求 (图片 A)。
- 服务器响应图片 A。
- 连接保持开放。
- …直到所有资源都加载完毕,或者达到超时时间,连接才关闭。
1.1 解决了连接建立/关闭的开销,但还有什么问题?
- 队头阻塞依然存在: 虽然连接是持久的,但请求和响应仍然是串行的。你必须等前一个请求的响应完全回来,才能发送下一个请求。这就像你和朋友打电话,虽然没挂断,但你们还是得轮流说话,不能同时说。
- 头部冗余: 每次请求都会发送大量的重复头部信息(如 User-Agent, Accept 等),浪费带宽。
- 没有服务器推送: 服务器只能被动响应,不能主动推送客户端可能需要的资源。
流程图:HTTP/1.1 请求流程 (持久连接)
#mermaid-svg-R2sxYSQLeK4XA02i {font-family:\"trebuchet ms\",verdana,arial,sans-serif;font-size:16px;fill:#333;}#mermaid-svg-R2sxYSQLeK4XA02i .error-icon{fill:#552222;}#mermaid-svg-R2sxYSQLeK4XA02i .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-R2sxYSQLeK4XA02i .edge-thickness-normal{stroke-width:2px;}#mermaid-svg-R2sxYSQLeK4XA02i .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-R2sxYSQLeK4XA02i .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-R2sxYSQLeK4XA02i .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-R2sxYSQLeK4XA02i .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-R2sxYSQLeK4XA02i .marker{fill:#333333;stroke:#333333;}#mermaid-svg-R2sxYSQLeK4XA02i .marker.cross{stroke:#333333;}#mermaid-svg-R2sxYSQLeK4XA02i svg{font-family:\"trebuchet ms\",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-R2sxYSQLeK4XA02i .label{font-family:\"trebuchet ms\",verdana,arial,sans-serif;color:#333;}#mermaid-svg-R2sxYSQLeK4XA02i .cluster-label text{fill:#333;}#mermaid-svg-R2sxYSQLeK4XA02i .cluster-label span{color:#333;}#mermaid-svg-R2sxYSQLeK4XA02i .label text,#mermaid-svg-R2sxYSQLeK4XA02i span{fill:#333;color:#333;}#mermaid-svg-R2sxYSQLeK4XA02i .node rect,#mermaid-svg-R2sxYSQLeK4XA02i .node circle,#mermaid-svg-R2sxYSQLeK4XA02i .node ellipse,#mermaid-svg-R2sxYSQLeK4XA02i .node polygon,#mermaid-svg-R2sxYSQLeK4XA02i .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-R2sxYSQLeK4XA02i .node .label{text-align:center;}#mermaid-svg-R2sxYSQLeK4XA02i .node.clickable{cursor:pointer;}#mermaid-svg-R2sxYSQLeK4XA02i .arrowheadPath{fill:#333333;}#mermaid-svg-R2sxYSQLeK4XA02i .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-R2sxYSQLeK4XA02i .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-R2sxYSQLeK4XA02i .edgeLabel{background-color:#e8e8e8;text-align:center;}#mermaid-svg-R2sxYSQLeK4XA02i .edgeLabel rect{opacity:0.5;background-color:#e8e8e8;fill:#e8e8e8;}#mermaid-svg-R2sxYSQLeK4XA02i .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-R2sxYSQLeK4XA02i .cluster text{fill:#333;}#mermaid-svg-R2sxYSQLeK4XA02i .cluster span{color:#333;}#mermaid-svg-R2sxYSQLeK4XA02i div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:\"trebuchet ms\",verdana,arial,sans-serif;font-size:12px;background:hsl(80, 100%, 96.2745098039%);border:1px solid #aaaa33;border-radius:2px;pointer-events:none;z-index:100;}#mermaid-svg-R2sxYSQLeK4XA02i :root{--mermaid-font-family:\"trebuchet ms\",verdana,arial,sans-serif;}客户端请求 HTML建立 TCP 连接发送 HTTP 请求 (HTML)服务器处理发送 HTTP 响应 (HTML)客户端解析 HTML
发现需要图片1在同一连接上
发送 HTTP 请求 (图片1)服务器处理发送 HTTP 响应 (图片1)重复上述过程
直到所有资源加载完毕
或连接超时关闭 TCP 连接
HTTP/2.0:彻底的性能革命
随着网页越来越复杂,资源越来越多,HTTP/1.1 的队头阻塞问题变得越来越突出。人们开始思考,有没有一种方式,能让一个连接同时处理多个请求和响应,就像多车道高速公路一样?这就是 HTTP/2.0 的核心思想。
HTTP/2.0 基于 Google 的 SPDY 协议,它在应用层和传输层之间增加了一个二进制分帧层。
-
多路复用(Multiplexing): 在一个 TCP 连接上,同时发送多个请求和接收多个响应,且请求和响应之间互不影响。
-
二进制分帧(Binary Framing): 所有通信都被分解为更小的、独立的帧,并以二进制格式传输。
-
头部压缩(Header Compression): 使用 HPACK 算法压缩 HTTP 头部,减少冗余数据传输。
-
服务器推送(Server Push): 服务器可以在客户端请求某个资源时,主动推送客户端可能需要的其他资源。
-
请求优先级(Request Prioritization): 客户端可以为请求设置优先级,服务器可以根据优先级决定响应顺序。
-
1.1 的痛点: 队头阻塞,头部冗余,无服务器推送。
-
如何解决队头阻塞?
- 多路复用: 把每个请求和响应都拆分成小块(帧),给每个帧一个唯一的标识符。然后这些帧可以在同一个 TCP 连接上乱序发送,接收方根据标识符再重新组装。这就像快递公司,把你的包裹拆成小件,然后和别人的小件一起装车,到了目的地再根据单号重新组装。
- 二进制分帧: 为什么是二进制?因为二进制解析效率高,更紧凑,不像文本协议那样需要复杂的解析。
-
如何解决头部冗余?
- 头部压缩: 很多请求的头部信息是重复的,比如 User-Agent。我们可以维护一个“字典”(索引表),把常用的头部信息存起来,下次只发送字典的索引号就行了。
-
如何提高加载速度?
- 服务器推送: 客户端请求 HTML 页面时,服务器知道这个页面肯定需要 CSS 和 JS 文件,那服务器就可以在客户端还没请求 CSS 和 JS 之前,就把它们“推”给客户端。这样客户端就不用再发请求了,节省了往返时间。
-
如何优化资源加载顺序?
- 请求优先级: 客户端可以告诉服务器,哪个资源更重要(比如 CSS 比图片更重要),服务器就可以优先处理重要的请求。
流程图:HTTP/2.0 请求流程
#mermaid-svg-XyqmlTW9cdO2fPhz {font-family:\"trebuchet ms\",verdana,arial,sans-serif;font-size:16px;fill:#333;}#mermaid-svg-XyqmlTW9cdO2fPhz .error-icon{fill:#552222;}#mermaid-svg-XyqmlTW9cdO2fPhz .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-XyqmlTW9cdO2fPhz .edge-thickness-normal{stroke-width:2px;}#mermaid-svg-XyqmlTW9cdO2fPhz .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-XyqmlTW9cdO2fPhz .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-XyqmlTW9cdO2fPhz .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-XyqmlTW9cdO2fPhz .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-XyqmlTW9cdO2fPhz .marker{fill:#333333;stroke:#333333;}#mermaid-svg-XyqmlTW9cdO2fPhz .marker.cross{stroke:#333333;}#mermaid-svg-XyqmlTW9cdO2fPhz svg{font-family:\"trebuchet ms\",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-XyqmlTW9cdO2fPhz .label{font-family:\"trebuchet ms\",verdana,arial,sans-serif;color:#333;}#mermaid-svg-XyqmlTW9cdO2fPhz .cluster-label text{fill:#333;}#mermaid-svg-XyqmlTW9cdO2fPhz .cluster-label span{color:#333;}#mermaid-svg-XyqmlTW9cdO2fPhz .label text,#mermaid-svg-XyqmlTW9cdO2fPhz span{fill:#333;color:#333;}#mermaid-svg-XyqmlTW9cdO2fPhz .node rect,#mermaid-svg-XyqmlTW9cdO2fPhz .node circle,#mermaid-svg-XyqmlTW9cdO2fPhz .node ellipse,#mermaid-svg-XyqmlTW9cdO2fPhz .node polygon,#mermaid-svg-XyqmlTW9cdO2fPhz .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-XyqmlTW9cdO2fPhz .node .label{text-align:center;}#mermaid-svg-XyqmlTW9cdO2fPhz .node.clickable{cursor:pointer;}#mermaid-svg-XyqmlTW9cdO2fPhz .arrowheadPath{fill:#333333;}#mermaid-svg-XyqmlTW9cdO2fPhz .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-XyqmlTW9cdO2fPhz .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-XyqmlTW9cdO2fPhz .edgeLabel{background-color:#e8e8e8;text-align:center;}#mermaid-svg-XyqmlTW9cdO2fPhz .edgeLabel rect{opacity:0.5;background-color:#e8e8e8;fill:#e8e8e8;}#mermaid-svg-XyqmlTW9cdO2fPhz .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-XyqmlTW9cdO2fPhz .cluster text{fill:#333;}#mermaid-svg-XyqmlTW9cdO2fPhz .cluster span{color:#333;}#mermaid-svg-XyqmlTW9cdO2fPhz div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:\"trebuchet ms\",verdana,arial,sans-serif;font-size:12px;background:hsl(80, 100%, 96.2745098039%);border:1px solid #aaaa33;border-radius:2px;pointer-events:none;z-index:100;}#mermaid-svg-XyqmlTW9cdO2fPhz :root{--mermaid-font-family:\"trebuchet ms\",verdana,arial,sans-serif;}客户端建立 TCP 连接 (一次)多路复用层请求 HTML (流1)请求 图片1 (流2)请求 CSS1 (流3)请求 JS1 (流4)二进制分帧
头部压缩二进制分帧
头部压缩二进制分帧
头部压缩二进制分帧
头部压缩所有帧在同一TCP连接上
乱序发送服务器接收帧
重新组装服务器处理 HTML (流1)服务器处理 图片1 (流2)服务器处理 CSS1 (流3)服务器处理 JS1 (流4)服务器推送
(例如: 发现HTML需要CSS2, JS2)发送 CSS2 (流5)发送 JS2 (流6)所有响应帧在同一TCP连接上
乱序发送客户端接收帧
重新组装客户端解析并渲染页面连接保持开放
直到所有通信结束
核心本质
- HTTP/1.0: 简单粗暴,每次任务独立完成。它的本质是串行处理,资源利用率低。
- HTTP/1.1: 在 1.0 基础上打了个补丁,通过持久连接减少了连接开销,但本质上还是串行请求-响应,只是在同一个管道里串行。
- HTTP/2.0: 彻底改变了传输方式,引入了多路复用。它的本质是并行处理,通过在应用层和传输层之间增加一个“调度层”(二进制分帧层),将逻辑上的多个流映射到物理上的一个 TCP 连接,从而解决了队头阻塞,并引入了更多优化手段。它不再是简单的“一问一答”,而是更像一个智能的“数据管道”。
继续思考
- 为什么不直接在 TCP 层解决队头阻塞? TCP 层的队头阻塞是数据包丢失重传导致的。如果一个 TCP 包丢失了,即使后面的包都收到了,TCP 也必须等待丢失的包重传成功并按序组装,才能把数据交给应用层。HTTP/2.0 的多路复用是在应用层实现的,它解决了 HTTP 层面(逻辑流)的队头阻塞,但无法解决 TCP 层面(物理包)的队头阻塞。这也是为什么 HTTP/3.0 转向 UDP (QUIC) 的原因之一,因为它想从传输层彻底解决队头阻塞。
- HTTP/2.0 的安全性: 虽然 HTTP/2.0 协议本身不强制加密,但几乎所有主流浏览器都只支持基于 TLS/SSL 的 HTTP/2.0 (即 HTTPS)。这使得 HTTP/2.0 在实践中比 HTTP/1.x 更安全。
- HTTP/2.0 的适用场景: 对于需要加载大量小文件、或者需要频繁与服务器交互的单页应用 (SPA) 等场景,HTTP/2.0 的性能优势尤为明显。
队头阻塞的本质是:在需要保证顺序性的系统中,如果“队头”的元素处理受阻,那么“队尾”的元素即使已经准备好,也无法越过队头被处理。
TCP 队头阻塞是为了保证数据包的可靠性和按序交付。如果一个数据包丢失,后续的数据包即使到达,也无法被应用层消费,因为 TCP 无法确定后续数据包的完整上下文。
HTTP/1.x 队头阻塞是因为 HTTP/1.x 协议设计上,在单个 TCP 连接中,请求和响应是严格串行且一一对应的。它没有机制来区分和并行处理不同的逻辑流。
流程图:HTTP/2.0 多路复用与流
#mermaid-svg-93YknzrX9pHBkGEC {font-family:\"trebuchet ms\",verdana,arial,sans-serif;font-size:16px;fill:#333;}#mermaid-svg-93YknzrX9pHBkGEC .error-icon{fill:#552222;}#mermaid-svg-93YknzrX9pHBkGEC .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-93YknzrX9pHBkGEC .edge-thickness-normal{stroke-width:2px;}#mermaid-svg-93YknzrX9pHBkGEC .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-93YknzrX9pHBkGEC .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-93YknzrX9pHBkGEC .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-93YknzrX9pHBkGEC .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-93YknzrX9pHBkGEC .marker{fill:#333333;stroke:#333333;}#mermaid-svg-93YknzrX9pHBkGEC .marker.cross{stroke:#333333;}#mermaid-svg-93YknzrX9pHBkGEC svg{font-family:\"trebuchet ms\",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-93YknzrX9pHBkGEC .label{font-family:\"trebuchet ms\",verdana,arial,sans-serif;color:#333;}#mermaid-svg-93YknzrX9pHBkGEC .cluster-label text{fill:#333;}#mermaid-svg-93YknzrX9pHBkGEC .cluster-label span{color:#333;}#mermaid-svg-93YknzrX9pHBkGEC .label text,#mermaid-svg-93YknzrX9pHBkGEC span{fill:#333;color:#333;}#mermaid-svg-93YknzrX9pHBkGEC .node rect,#mermaid-svg-93YknzrX9pHBkGEC .node circle,#mermaid-svg-93YknzrX9pHBkGEC .node ellipse,#mermaid-svg-93YknzrX9pHBkGEC .node polygon,#mermaid-svg-93YknzrX9pHBkGEC .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-93YknzrX9pHBkGEC .node .label{text-align:center;}#mermaid-svg-93YknzrX9pHBkGEC .node.clickable{cursor:pointer;}#mermaid-svg-93YknzrX9pHBkGEC .arrowheadPath{fill:#333333;}#mermaid-svg-93YknzrX9pHBkGEC .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-93YknzrX9pHBkGEC .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-93YknzrX9pHBkGEC .edgeLabel{background-color:#e8e8e8;text-align:center;}#mermaid-svg-93YknzrX9pHBkGEC .edgeLabel rect{opacity:0.5;background-color:#e8e8e8;fill:#e8e8e8;}#mermaid-svg-93YknzrX9pHBkGEC .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-93YknzrX9pHBkGEC .cluster text{fill:#333;}#mermaid-svg-93YknzrX9pHBkGEC .cluster span{color:#333;}#mermaid-svg-93YknzrX9pHBkGEC div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:\"trebuchet ms\",verdana,arial,sans-serif;font-size:12px;background:hsl(80, 100%, 96.2745098039%);border:1px solid #aaaa33;border-radius:2px;pointer-events:none;z-index:100;}#mermaid-svg-93YknzrX9pHBkGEC :root{--mermaid-font-family:\"trebuchet ms\",verdana,arial,sans-serif;}客户端建立单个 TCP 连接HTTP/2.0 二进制分帧层创建流1: 请求图片A创建流2: 请求CSS文件创建流3: 请求JS文件图片A帧1 (流1)图片A帧2 (流1)CSS帧1 (流2)JS帧1 (流3)所有帧在TCP连接上
交错发送 (乱序)服务器
接收交错帧HTTP/2.0 二进制分帧层
根据流ID重组帧重组流1: 完整图片A请求重组流2: 完整CSS请求重组流3: 完整JS请求服务器处理请求
(并行处理)生成流1响应帧生成流2响应帧生成流3响应帧所有响应帧在TCP连接上
交错发送 (乱序)客户端
接收交错帧HTTP/2.0 二进制分帧层
根据流ID重组帧交付应用层: 图片A响应交付应用层: CSS响应交付应用层: JS响应O1,O2,O3F,L
为什么 HTTP/2.0 能解决 HTTP 层的队头阻塞?
HTTP/2.0 引入了多路复用。它把每个 HTTP 请求和响应都看作一个独立的“流”(Stream),每个流都有自己的 ID。这些流的数据被拆分成更小的“帧”,这些帧可以在同一个 TCP 连接上乱序发送。接收方根据帧的 ID 重新组装成完整的流。HTTP/2.0 解决了应用层(HTTP 协议层面)的队头阻塞,因为它不再强制请求和响应的串行顺序。
HTTP/2.0 为什么不能解决 TCP 层的队头阻塞?
HTTP/2.0 仍然是基于 TCP 协议的。如果底层的 TCP 连接中,某个数据包丢失了,那么整个 TCP 连接仍然会因为等待这个丢失的数据包重传而暂停,这会影响到所有在当前 TCP 连接上跑的 HTTP/2.0 流。这就像电话会议(HTTP/2.0)开得很好,但如果电话线(TCP 连接)断了,所有人都受影响。
HTTP/3.0 如何解决 TCP 层的队头阻塞?
HTTP/3.0 放弃了 TCP,转而使用基于 UDP 的 QUIC 协议。QUIC 协议在传输层实现了自己的可靠传输和多路复用机制。它为每个逻辑流分配独立的序列号,这样即使一个流的数据包丢失,也只会影响到这一个流,而不会阻塞其他流的数据传输。这就像,你和朋友们在不同的房间里打电话,即使一个房间的电话线断了,其他房间的通话也不受影响。
HTTP/2.0 是对 HTTP/1.x 的一次彻底的性能优化 对于HTTP/1.x,在服务器处理某个请求资源回复慢时,会阻塞其他请求,而2.0使用流的逻辑标识,可以同时发不同的请求,尽管带宽是一样的,却可以降低延迟,更高效率的利用网络传输通道,它将TCP的流组合成不同的请求数据,解决了应用层的队头阻塞,可是依旧是基于TCP流的,这意味,由于网络层面的TCP出错依旧会队头阻塞,并没有解决本质问题
而3.0彻底解决了这个问题,3.0不再使用TCP而是UDP,每个请求都是额外的流,QUIC协议可以将传输和加密的握手结合在一起,如果客户端之前连接过服务器,并且服务器支持,甚至可以实现 0-RTT 握手,即客户端在发送第一个数据包时就包含应用数据,几乎没有延迟。 TCP 连接是基于 IP 地址和端口号的。如果你的设备从 Wi-Fi 切换到移动数据(IP 地址变化),TCP 连接就会断开,需要重新建立。 QUIC 连接是基于一个 64 位的连接 ID。即使客户端的 IP 地址或端口号发生变化,只要连接 ID 不变,QUIC 连接就可以保持活跃,无需重新建立。
流程图:HTTP/3.0 (QUIC) 解决队头阻塞
#mermaid-svg-4o6aUgFNNrm3CFuf {font-family:\"trebuchet ms\",verdana,arial,sans-serif;font-size:16px;fill:#333;}#mermaid-svg-4o6aUgFNNrm3CFuf .error-icon{fill:#552222;}#mermaid-svg-4o6aUgFNNrm3CFuf .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-4o6aUgFNNrm3CFuf .edge-thickness-normal{stroke-width:2px;}#mermaid-svg-4o6aUgFNNrm3CFuf .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-4o6aUgFNNrm3CFuf .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-4o6aUgFNNrm3CFuf .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-4o6aUgFNNrm3CFuf .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-4o6aUgFNNrm3CFuf .marker{fill:#333333;stroke:#333333;}#mermaid-svg-4o6aUgFNNrm3CFuf .marker.cross{stroke:#333333;}#mermaid-svg-4o6aUgFNNrm3CFuf svg{font-family:\"trebuchet ms\",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-4o6aUgFNNrm3CFuf .label{font-family:\"trebuchet ms\",verdana,arial,sans-serif;color:#333;}#mermaid-svg-4o6aUgFNNrm3CFuf .cluster-label text{fill:#333;}#mermaid-svg-4o6aUgFNNrm3CFuf .cluster-label span{color:#333;}#mermaid-svg-4o6aUgFNNrm3CFuf .label text,#mermaid-svg-4o6aUgFNNrm3CFuf span{fill:#333;color:#333;}#mermaid-svg-4o6aUgFNNrm3CFuf .node rect,#mermaid-svg-4o6aUgFNNrm3CFuf .node circle,#mermaid-svg-4o6aUgFNNrm3CFuf .node ellipse,#mermaid-svg-4o6aUgFNNrm3CFuf .node polygon,#mermaid-svg-4o6aUgFNNrm3CFuf .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-4o6aUgFNNrm3CFuf .node .label{text-align:center;}#mermaid-svg-4o6aUgFNNrm3CFuf .node.clickable{cursor:pointer;}#mermaid-svg-4o6aUgFNNrm3CFuf .arrowheadPath{fill:#333333;}#mermaid-svg-4o6aUgFNNrm3CFuf .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-4o6aUgFNNrm3CFuf .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-4o6aUgFNNrm3CFuf .edgeLabel{background-color:#e8e8e8;text-align:center;}#mermaid-svg-4o6aUgFNNrm3CFuf .edgeLabel rect{opacity:0.5;background-color:#e8e8e8;fill:#e8e8e8;}#mermaid-svg-4o6aUgFNNrm3CFuf .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-4o6aUgFNNrm3CFuf .cluster text{fill:#333;}#mermaid-svg-4o6aUgFNNrm3CFuf .cluster span{color:#333;}#mermaid-svg-4o6aUgFNNrm3CFuf div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:\"trebuchet ms\",verdana,arial,sans-serif;font-size:12px;background:hsl(80, 100%, 96.2745098039%);border:1px solid #aaaa33;border-radius:2px;pointer-events:none;z-index:100;}#mermaid-svg-4o6aUgFNNrm3CFuf :root{--mermaid-font-family:\"trebuchet ms\",verdana,arial,sans-serif;}客户端服务器HTTP 应用层
(Web 服务器)HTTP/3.0 (QUIC) 层HTTP/3.0 (QUIC) 层HTTP 应用层
(浏览器)创建 QUIC 流1: 请求图片A创建 QUIC 流2: 请求CSS文件创建 QUIC 流3: 请求JS文件流1数据包
(独立序列号)流2数据包
(独立序列号)流3数据包
(独立序列号)所有流的数据包
在单个UDP连接上
并行传输服务器接收流1数据包
(独立处理重传)服务器接收流2数据包
(独立处理重传)服务器接收流3数据包
(独立处理重传)处理请求1处理请求2处理请求3响应1 (流1)响应2 (流2)响应3 (流3)所有流的响应数据包
在单个UDP连接上
并行传输客户端接收流1数据包
(独立处理重传)客户端接收流2数据包
(独立处理重传)客户端接收流3数据包
(独立处理重传)交付应用层: 响应1交付应用层: 响应2交付应用层: 响应3A,GK1,K2,K3E,ID1,D2,D3,F1,F2,F3,H1,H2,H3,J1,J2,J3
UDP本身并不会有失败重发的机制,所以即使是一个UDP连接网络抖动不会影响整个UDP连接的数据重发,而是在QUIC协议负责每个流的重发,QUIC实现都是在应用层的,也因此对操作系统内核没有额外要求,不需要更改,把TCP的可靠性实现由操作系统内核上升到应用层去实现这些