← 返回题目列表

什么是队头阻塞(HOL Blocking)?HTTP/1.1、HTTP/2、HTTP/3 分别怎么解决?

高频 中等 第 4 / 27 题 更新于 2026/07/28
队头阻塞HTTP/2HTTP/3QUIC

简化版

队头阻塞(Head-of-Line Blocking)指排在队伍最前面的那个请求/数据卡住了,后面的全都得跟着等,哪怕后面的早就准备好了。HTTP 里它出现在两个层面:HTTP 层——HTTP/1.1 一条连接同一时刻只能处理一个请求,前一个响应没回来后面的就排队;TCP 层——TCP 要求字节严格按序交付,中间某个包丢了,后面的包即使到了也得在内核缓冲区里等重传。HTTP/2 用多路复用解决了 HTTP 层的队头阻塞(一条连接并发多个流),但因为仍跑在 TCP 上,没解决 TCP 层的队头阻塞HTTP/3 换用基于 UDP 的 QUIC,让每个流独立管理丢包重传,才把 TCP 层的队头阻塞也解决了

详细版

队头阻塞的两个层面:

层面谁受影响出现在
HTTP 层队头阻塞应用请求排队HTTP/1.1
TCP 层队头阻塞一个流的丢包拖累所有流HTTP/1.1、HTTP/2(都跑在 TCP 上)

① HTTP/1.1 的队头阻塞

HTTP/1.1 一条 TCP 连接上,请求必须一问一答:发出请求 A,要等 A 的响应完整回来,才能发请求 B。如果 A 的响应很慢(比如后端在算一个大报表),B、C、D 全都干等着。虽然浏览器靠开 6 个并发连接缓解,但连接数有限,本质没解决。(HTTP/1.1 曾有个 Pipelining 想让请求批量发出,但服务端仍要按顺序返回响应,队头阻塞照旧,且兼容性差,基本没人用。)

② HTTP/2 的解决:多路复用

HTTP/2 引入流(Stream)帧(Frame):一条 TCP 连接上可以有很多个流并发,每个请求/响应拆成帧,帧上带 Stream ID,交错着传,到对端再按 ID 重新组装。这样请求 A 慢,请求 B 的帧照样能穿插着发回来——HTTP 层的队头阻塞解决了

③ HTTP/2 仍存在 TCP 层队头阻塞

HTTP/2 的所有流共用一条 TCP 连接。TCP 保证字节严格按序交付:如果流 A 的一个 TCP 包丢了,TCP 会卡住整条连接的数据交付(后面的包哪怕属于流 B、已经到了,也得在内核里排队等 A 的包重传补齐)。于是「一个流丢包,全部流卡住」——TCP 层的队头阻塞。在丢包率高的网络上,HTTP/2 有时反而不如多连接的 HTTP/1.1。

④ HTTP/3 的解决:QUIC over UDP

HTTP/3 抛弃 TCP,改用 QUIC(跑在 UDP 上,自己实现可靠传输)。QUIC 在协议层就理解「流」的概念,每个流独立编号、独立管理丢包和重传:流 A 丢包只需重传流 A 的数据,流 B 的数据可以照常交付,互不干扰。这样TCP 层的队头阻塞也被消除

完整版教学

一、先建立直觉:什么是「队头阻塞」

想象超市只开了一个收银台,排在最前面的顾客要退货、要理论半天,后面所有人不管东西多少、多急,都得等。这就是队头阻塞:串行队列里,队头的慢会拖累整条队伍

网络里凡是「必须按顺序处理」的地方,都可能出现队头阻塞。HTTP 的性能演进史,很大程度上就是一步步消灭队头阻塞的历史。要讲清楚,必须分清它藏在两个不同的层

二、HTTP/1.1:应用层的队头阻塞

HTTP/1.1 的连接模型是请求-响应串行。一条 TCP 连接上,客户端发出请求后,必须等这个请求的响应完整回来,才能在这条连接上发下一个请求。

连接上的时间线(HTTP/1.1):
请求A ───────────► 
                 ◄─────── 响应A(很慢)
请求B ───────────►                       ← B 一直等到 A 回完才能发
                        ◄─────── 响应B

为什么浏览器还挺快? 因为浏览器对同一域名开多条并发连接(主流浏览器默认 6 条)。6 条连接 = 6 个收银台,能同时处理 6 个请求。但这带来新问题:

  • 连接数有限(6 条),页面上几十上百个资源仍要排队。
  • 每条连接都要独立三次握手 + TLS 握手 + 慢启动,开销大。
  • 为了突破 6 条限制,前端搞出域名分片(把资源分散到 img1./img2. 等多个子域)等 hack。

HTTP/1.1 的队头阻塞是**「一条连接同一时刻只能跑一个请求」**造成的应用层排队,本质是协议模型的限制。

三、HTTP/2:多路复用消灭 HTTP 层队头阻塞

HTTP/2 把一条连接逻辑上切成很多并行的「流(Stream)」

  • 流(Stream):一次请求-响应就是一个流,有唯一的 Stream ID。
  • 帧(Frame):HTTP/2 传输的最小单位。请求头拆成 HEADERS 帧,响应体拆成一个个 DATA 帧,每个帧头部都标着自己属于哪个 Stream ID。
  • 多路复用:不同流的帧可以交错在同一条连接上发送,接收端根据 Stream ID 把帧重新归类、组装成完整的请求/响应。
一条 TCP 连接(HTTP/2):
… [流1-DATA][流3-HEADERS][流1-DATA][流5-DATA][流3-DATA] …
   交错传输,接收端按 Stream ID 各自归位

这样,即使流 1 的响应在后端算得慢,流 3、流 5 的帧照样能穿插发回来——应用层的队头阻塞被消除,一条连接就能高效并发,还顺带省掉了多连接的握手开销。HTTP/2 还带了头部压缩(HPACK)、服务器推送、流优先级等特性。

四、致命遗留:HTTP/2 仍卡在 TCP 层

HTTP/2 的多路复用是在 TCP 之上做的,所有流共享同一条 TCP 连接。而 TCP 有一条铁律:向应用交付的字节必须严格按序、不能有洞

问题就来了:TCP 把这条连接上的所有数据看成一条字节流,它根本不知道上层有「流」的划分。如果某个 TCP 段丢了:

TCP 收到的段: [seg1][seg2][×丢失×][seg4][seg5]
                                    ↑ seg4、seg5 已到,但因为 seg3 没到,
                                      TCP 不能交付它们,全压在内核缓冲区等 seg3 重传

即使 seg4、seg5 属于完全不相干的另一个流、而且已经安全抵达,TCP 也不会把它们交给应用——因为按序交付的规矩不允许「跳过 seg3 先给后面的」。于是一个流的一个丢包,冻结了这条连接上所有流的数据交付。这就是 TCP 层队头阻塞

后果:在丢包率高、网络差的环境(如移动网络、跨国链路),HTTP/2 单连接的这个特性反而成了短板,有时体验不如「多条连接、丢包只影响其中一条」的 HTTP/1.1。

五、HTTP/3:QUIC 从根上解决

要根治 TCP 层队头阻塞,就不能再用 TCP 那套「整条连接一把梭按序交付」的模型。HTTP/3 的做法是换掉 TCP,改用 QUIC——一个跑在 UDP 之上、自己实现可靠传输和拥塞控制的协议。

关键区别:QUIC 在协议内部就有「流」的概念,可靠性是按流独立管理的

  • 每个 QUIC 流有自己独立的序号空间和重传逻辑。
  • 流 A 丢了一个包,QUIC 只需重传流 A 的那部分,流 B、流 C 的数据该交付照样交付,完全不受流 A 影响。
QUIC(HTTP/3):
流A:[a1][×a2×][a3]   ← a2 丢了,只有流A卡住等重传
流B:[b1][b2][b3]     ← 照常交付,不受影响
流C:[c1][c2]         ← 照常交付

这样TCP 层的队头阻塞被彻底消除。QUIC 还顺带带来:更快的连接建立(把传输握手和 TLS 握手合并,0-RTT/1-RTT)、连接迁移(用 Connection ID 标识连接,手机从 WiFi 切 4G、IP 变了连接也不断)。

注意:QUIC 消除的是传输层的队头阻塞。如果用了 HTTP 头部压缩(QPACK)且存在跨流依赖,理论上仍可能有极小的「HTTP 层」阻塞,但 QPACK 已针对性设计以规避,工程上可忽略。

六、三代对比总表

HTTP/1.1HTTP/2HTTP/3
传输层TCPTCPQUIC / UDP
并发方式多条连接串行单连接多路复用单连接多路复用
HTTP 层队头阻塞❌ 有✅ 解决✅ 解决
TCP 层队头阻塞❌ 有仍有解决
头部压缩HPACKQPACK
连接建立TCP + TLS 分开握手TCP + TLSQUIC 握手合并,可 0-RTT

七、常见误区与追问

考点正确口径
TCP 队头阻塞同一字节流前面丢包会阻塞后续字节交付
HTTP/1.1 队头阻塞同连接请求响应顺序限制
HTTP/2 问题多路复用解决应用层排队,但仍受 TCP 丢包阻塞
HTTP/2 streams over one TCP connection:
stream 1 frame lost at TCP seq=1000
stream 2 frame seq=1400 arrived
TCP cannot deliver seq=1400 to app until seq=1000 retransmitted

HTTP/2 解决了 HTTP 层队头阻塞,但没有解决 TCP 字节流的队头阻塞;QUIC 才把阻塞粒度降到流级别。

  • 误区:HTTP/2 已经彻底消灭队头阻塞。 它消灭了 HTTP/1.1 请求排队,但 TCP 丢包仍会阻塞同连接上的所有流。
  • 误区:队头阻塞只发生在 HTTP。 任何有序队列或字节流都可能出现前一个元素阻塞后续元素。
  • 误区:多开连接一定最好。 多连接可缓解阻塞,但会增加握手、拥塞控制竞争和资源消耗。
  • 追问:QUIC 如何改善? QUIC 在 UDP 上实现多流,某个流丢包通常不阻塞其它流的数据交付。
  • 追问:HTTP/1.1 管线化为什么少用? 响应必须按序返回,一个慢响应会挡住后续响应,队头阻塞严重。
  • 追问:丢包对 HTTP/2 为什么影响大? 多个流复用一个 TCP 连接,一个 TCP 序号缺口会卡住整个连接的数据交付。

八、加强记忆

队头阻塞就是队头卡住,后面全等。HTTP 里分两层:HTTP 层(HTTP/1.1 一条连接一次只跑一个请求)和 TCP 层(TCP 要求按序交付,一个包丢了后面全等重传)。HTTP/2 用多路复用干掉了 HTTP 层的队头阻塞,但因为还跑在 TCP 上,TCP 层的队头阻塞没解决HTTP/3 改用 UDP 上的 QUIC,让每个流独立重传,才把 TCP 层的队头阻塞也消除。记一条主线:多路复用治应用层,换掉 TCP 治传输层