HTTP1.x 到 HTTP2 的基因革命二进制分帧HPACK压缩服务端推送三核解析

201 阅读10分钟

公众号:小博的前端笔记

1、HTTP/1.x 的核心瓶颈

20250624_f4329a.png 0. 队头阻塞(Head-of-Line Blocking)

-   同一 TCP 连接中,前一个请求未完成时,后续请求被阻塞。

0. 冗余头部(Header Redundancy)

-   每次请求携带重复 Cookie/User-Agent 等头部(未压缩)。

0. 连接数限制

-   浏览器为突破队头阻塞,建立 6~8 个 TCP 连接并行请求,导致 TCP 握手开销大。

2、HTTP/2 的五大革新(面试核心考点)

1. ⚡ 二进制分帧(Binary Framing)

  • 变革:用二进制格式(帧 + 流)替代 HTTP/1.x 的文本格式。

  • 优势

    • 解析效率提升(无需文本转换)
    • 支持多路复用(Multiplexing) → 彻底解决队头阻塞
// 帧结构示例(非代码,仅示意)
+-----------------------------------------------+
| Length (24 bits) | Type (8 bits) | Flags (8) |
| Stream ID (31 bits) | Frame Payload (可变长度) |
+-----------------------------------------------+

2. 🔁 多路复用(Multiplexing)

  • 机制:单 TCP 连接上并行传输多个请求/响应(交错发送帧)。

  • 前端收益

    • 减少 TCP 连接数(避免域名分片等 Hack)
    • 提升页面加载速度(尤其高延迟网络)

20250624_a16ada.png

3. 🗜️ 头部压缩(HPACK)

  • 机制

    • 静态 Huffman 编码 + 动态索引表(客户端/服务端维护字典)
    • 首次请求发送完整 Header,后续只传索引 ID
  • 效果:头部体积减少 60%~90% (尤其对 Cookie 重的应用显著)

4. 🚀 服务端推送(Server Push)

  • 机制:服务端主动推送客户端未请求的资源(如 CSS/JS)。

  • 前端应用

    # Nginx 配置示例
    location /index.html {
       http2_push /style.css;
       http2_push /app.js;
    }
    
  • 注意:需权衡推送策略(避免浪费带宽)。

5. 🔒 强化安全(非强制但事实标准)

  • 主流浏览器(Chrome/Firefox)只支持 HTTP/2 over TLS
  • 推动全站 HTTPS 普及。

3、前端性能优化实战价值

场景HTTP/1.1 方案HTTP/2 方案收益
资源合并合并 CSS/JS(减少请求数)无需合并(小文件独立)提升缓存利用率
域名分片a.example.com b.example.com单域名即可简化 DNS 解析
图片精灵(Sprites)合并小图标独立加载按需更新,减少传输体积

最佳实践:HTTP/2 下应避免资源合并(雪碧图除外)、域名分片等 HTTP/1.x 时代的优化手段。


4、面试高频问题与回答技巧

  1. Q:HTTP/2 如何解决队头阻塞?

    💡 答: "通过二进制分帧层将请求/响应拆分为互不依赖的帧,并在单 TCP 连接上多路复用,实现并行传输,彻底规避了 HTTP/1.x 的请求级阻塞。"

  2. Q:服务端推送会取代 Webpack 的 code splitting 吗?

    💡 答: "不会互补而非替代。Server Push 解决的是关键资源的提前加载(如首屏 CSS),而 Code Splitting 解决的是按需加载非关键代码(如路由组件)。两者结合可实现最优性能。"

  3. Q:为什么 HTTP/2 仍需域名收敛?

    💡 答: "尽管多路复用允许单连接并行请求,但 DNS 查询和 TCP 握手仍有开销。建议将资源集中在 1~2 个域名下(如 static.example.com),平衡连接复用与 CDN 优化。"

  4. Q:HTTP/2 有缺点吗?

    💡 答: "TCP 层队头阻塞仍未解决(一个丢包阻塞所有流),这正是 HTTP/3 使用 QUIC 协议的原因。此外,服务端推送若策略不当可能浪费带宽。"


5、兼容性现状(2025年)

  • 浏览器支持:全球 > 98% (IE11 及旧版 Android 除外)

  • 部署建议

    # Nginx 启用 HTTP/2
    listen 443 ssl http2; 
    
    • 同时保留 HTTP/1.1 回退方案(Alt-Svc 头部)

6、总结:

“HTTP/2 通过二进制分帧多路复用解决了 HTTP/1.x 的队头阻塞问题,配合HPACK 头部压缩降低传输开销,而服务端推送进一步优化关键资源加载。作为前端,我们应调整性能优化策略(如放弃资源合并),充分利用新协议特性。”

7、什么是域名分片

技术原理

  • 是什么:将网站静态资源(如图片、JS、CSS)分散到多个子域名下(如 static1.example.comstatic2.example.com)。
  • HTTP/1.x 背景: 浏览器对同一域名的并发请求数有限制(Chrome: 6个),超出限制的请求会被阻塞(队头阻塞)。

在 HTTP/1.x 中的作用

20250624_ed0f94.png 0. 突破并发限制

-   若资源分布在 2 个子域名,浏览器总并发数 = `6 × 2 = 12`,显著提升并行加载能力。

0. 优化加载速度

-   关键用例:电商首页加载 50+ 商品图片时,分片可减少排队延迟。

HTTP/2 下的致命缺陷

问题原因
额外 DNS 查询每增加一个子域名需额外 DNS 解析(增加 20~200ms 延迟)
TCP 握手开销倍增每个子域名需独立 TCP + TLS 握手(增加 1~2 RTT)
破坏多路复用优势HTTP/2 单连接即可并行传输,分片反而强制创建多连接,浪费资源

HTTP/2 最佳实践域名收敛(所有资源集中在 static.example.com),最大化复用单连接。

提问:前端通过一个nginx地址进行转发还算域名分片

  • 不算域名分片

    。原因如下:

    • 前端视角:浏览器只向一个域名(Nginx地址)发起请求,因此浏览器对该域名应用标准并发连接限制(如6-8个连接)。不会触发域名分片的“多域名并发”效果,因为所有请求都指向同一个源。
    • Nginx转发是内部代理行为:Nginx将请求路由到不同后端域名,但这相当于“服务端重定向”或“代理”,而不是前端直接请求多个域名。摘要4和摘要7强调,Nginx的域名分发配置(如基于 server_name 的转发)是服务端逻辑,不改变前端请求的域。
    • 与传统分片对比:传统域名分片要求前端代码显式指定多个域名(如 <img src="https://static1.example.com/image.jpg">),浏览器会为每个域名建立独立连接。在您的设置中,前端没有这种多域名引用,资源加载的并发效率仅受限于单一Nginx域名。

8、HTTP/2 的多路复用最多支持多少个

HTTP/2 的多路复用(Multiplexing) 在单条 TCP 连接上支持近乎无限的并发流(Streams) ,但实际可用数量受协议规范、浏览器/服务器实现和网络环境三重限制。以下是分层解析:

一、协议规范层级(RFC 7540)

  1. 流标识符(Stream ID)

    • 范围:1 到 2^31-1(约 21 亿)
    • 流 ID 为 31 位整数(最高位保留)

20250624_748263.png

  1. 关键约束

    • 主动创建方(如浏览器)需严格按顺序分配 ID(1, 3, 5...)
    • ID 不能复用:一旦分配,即使流关闭,ID 也永久占用
    • 流量控制窗口:每个流有独立接收窗口(默认 64KB),可能阻塞

二、实际实现限制(主流环境 2025 年)

环境最大并发流数配置方式
浏览器
Chrome/Firefox100 ~ 250动态调整(根据网络质量)
Safari100(保守策略)不可配置
服务器
Nginx默认 128(可调至 1000+)http2_max_requests
Apache默认 100(可调至 1000)H2MaxStreams
Node.js依赖 nghttp2 库(默认 100)maxSessionMemory

⚠️ 注意:并发流 ≠ 并行传输速度,受以下因素制约:

  1. TCP 拥塞控制(带宽限制)
  2. 接收窗口大小(默认 64KB/流)
  3. 优先级竞争(高优先级流可抢占资源)

三、浏览器并发流测试(DevTools 验证)

在 Chrome 中模拟测试:

  1. 访问 HTTP/2 测试页(如 http2.golang.org/
  2. 发起 150 个图片请求,观察瀑布流:

20250624_ec582e.png

  • 现象:前 100 个流立即发送,后续流需等待空闲

四、突破限制的技术方案

1. 调大服务器配置(Nginx 示例)

http {
  http2_max_requests 1000;       # 最大流数
  http2_recv_buffer_size 256k;   # 增大接收缓冲区
}

2. 优化流优先级(HTTP/2 Priority)

:method: GET
:path: /critical.js
:authority: example.com
priority: u=3, i                 # 最高优先级(权重=3,独占模式)

3. 避免长尾流阻塞

  • 快速关闭流:服务端及时发送 RST_STREAM 终止非关键流
  • 动态窗口调整:通过 WINDOW_UPDATE 帧动态扩窗

五、与 HTTP/1.x 的并发对比

指标HTTP/1.1HTTP/2
连接数6~8 个 TCP 连接/域名1 个 TCP 连接
实际并发请求6~8 个/连接(总计 ~50)100~250 个流/连接
队头阻塞请求级阻塞(严重)帧级阻塞(轻微)

本质区别: HTTP/1.x 的并发受限于 TCP 连接数,而 HTTP/2 的并发受限于 流管理能力

六、提问

Q:HTTP/2 多路复用最多支持多少并发?

💡 : “从协议看,流 ID 范围 1~2^31-1 理论上支持数十亿流。但实际并发受三方限制

  1. 浏览器(如 Chrome 默认 100 流)
  2. 服务器(如 Nginx 默认 128 流)
  3. 网络(窗口大小、带宽竞争) 实践中,单个连接通常维持 100~250 个活跃流。超出时新流会排队,但可通过调整服务器配置和优化优先级策略提升吞吐量。”

延伸考点

  • 如何监控流阻塞?(答:用 DevTools 瀑布流看帧排队)
  • 为什么 HTTP/3 用 QUIC 替代 TCP?(答:解决 TCP 层队头阻塞)

六、活跃流是什么

在HTTP/2协议中,活跃流(Active Stream) 是指在一个TCP连接上同时处于打开状态、正在传输或等待处理的虚拟双向数据通道。它是HTTP/2实现多路复用(Multiplexing)的核心机制,彻底改变了HTTP/1.x时代的并发模型。以下是其核心要点及技术解析:

6.1🔍 一、活跃流的本质与定义
  1. 流(Stream)的概念

    • HTTP/2将每个请求/响应分解为独立的二进制帧(Frame) ,通过流标识符(Stream ID) 归属到不同逻辑通道中。
    • 活跃流特指处于OPEN状态的流,即已创建但未关闭(未收到END_STREAM标志或RST_STREAM帧)的流。
  2. 关键特性

    • 双向性:客户端和服务端可同时发送数据帧(如HEADERSDATA)。
    • 并发性:单TCP连接上可并行存在多个活跃流,帧按流ID分组处理,互不阻塞。
    • 有序性:同一流内的帧必须按顺序处理,但不同流的帧可交错传输。
⚙️ 6.2、活跃流的协议层约束
1. 数量限制机制
  • SETTINGS_MAX_CONCURRENT_STREAMS参数: 连接建立时,客户端和服务端通过SETTINGS帧协商最大活跃流数量4710。

    • RFC 7540建议值:不小于10078。

    • 典型默认值

      环境默认值可调整范围
      Chrome/Firefox100~250动态调整
      Nginx128可调至1000+
      Node.js100依赖底层库 610
2. 流标识符管理
  • Stream ID为31位整数(范围1~2³¹-1,约21亿),按奇偶分配(客户端奇数/服务端偶数)。
  • 不可复用:流关闭后其ID永久占用,耗尽需重建连接。
🧩 3、活跃流的状态与生命周期

20250624_03883d.png

  • OPEN状态:流正在传输数据(如DATA帧),受流量控制窗口约束。
  • 阻塞场景:若流窗口耗尽(默认64KB)或达到SETTINGS_MAX_CONCURRENT_STREAMS上限,新流需排队
⚖️ 4、流量控制对活跃流的影响

HTTP/2在应用层实现流控(Flow Control),解决多流协同问题:

  1. 窗口机制

    • 每个活跃流有独立接收窗口(默认65,535字节),发送DATA帧后窗口减小。
    • 接收方通过WINDOW_UPDATE帧动态扩窗(最大2³²-1字节)。
  2. 阻塞逻辑

    • 若流窗口=0,其DATA帧发送被暂停,但其他窗口未耗尽的流仍可传输。
    • 连接级窗口(Stream ID=0)限制所有流总和。