公众号:小博的前端笔记
1、HTTP/1.x 的核心瓶颈
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)
- 提升页面加载速度(尤其高延迟网络)
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、面试高频问题与回答技巧
-
Q:HTTP/2 如何解决队头阻塞?
💡 答: "通过二进制分帧层将请求/响应拆分为互不依赖的帧,并在单 TCP 连接上多路复用,实现并行传输,彻底规避了 HTTP/1.x 的请求级阻塞。"
-
Q:服务端推送会取代 Webpack 的 code splitting 吗?
💡 答: "不会互补而非替代。Server Push 解决的是关键资源的提前加载(如首屏 CSS),而 Code Splitting 解决的是按需加载非关键代码(如路由组件)。两者结合可实现最优性能。"
-
Q:为什么 HTTP/2 仍需域名收敛?
💡 答: "尽管多路复用允许单连接并行请求,但 DNS 查询和 TCP 握手仍有开销。建议将资源集中在 1~2 个域名下(如
static.example.com),平衡连接复用与 CDN 优化。" -
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.com、static2.example.com)。 - HTTP/1.x 背景: 浏览器对同一域名的并发请求数有限制(Chrome: 6个),超出限制的请求会被阻塞(队头阻塞)。
▶ 在 HTTP/1.x 中的作用
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)
-
流标识符(Stream ID)
- 范围:1 到 2^31-1(约 21 亿)
- 流 ID 为 31 位整数(最高位保留)
-
关键约束:
- 主动创建方(如浏览器)需严格按顺序分配 ID(1, 3, 5...)
- ID 不能复用:一旦分配,即使流关闭,ID 也永久占用
- 流量控制窗口:每个流有独立接收窗口(默认 64KB),可能阻塞
二、实际实现限制(主流环境 2025 年)
| 环境 | 最大并发流数 | 配置方式 |
|---|---|---|
| 浏览器 | ||
| Chrome/Firefox | 100 ~ 250 | 动态调整(根据网络质量) |
| Safari | 100(保守策略) | 不可配置 |
| 服务器 | ||
| Nginx | 默认 128(可调至 1000+) | http2_max_requests |
| Apache | 默认 100(可调至 1000) | H2MaxStreams |
| Node.js | 依赖 nghttp2 库(默认 100) | maxSessionMemory |
⚠️ 注意:并发流 ≠ 并行传输速度,受以下因素制约:
- TCP 拥塞控制(带宽限制)
- 接收窗口大小(默认 64KB/流)
- 优先级竞争(高优先级流可抢占资源)
三、浏览器并发流测试(DevTools 验证)
在 Chrome 中模拟测试:
- 访问 HTTP/2 测试页(如 http2.golang.org/)
- 发起 150 个图片请求,观察瀑布流:
- 现象:前 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.1 | HTTP/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 理论上支持数十亿流。但实际并发受三方限制:
- 浏览器(如 Chrome 默认 100 流)
- 服务器(如 Nginx 默认 128 流)
- 网络(窗口大小、带宽竞争) 实践中,单个连接通常维持 100~250 个活跃流。超出时新流会排队,但可通过调整服务器配置和优化优先级策略提升吞吐量。”
延伸考点:
- 如何监控流阻塞?(答:用 DevTools 瀑布流看帧排队)
- 为什么 HTTP/3 用 QUIC 替代 TCP?(答:解决 TCP 层队头阻塞)
六、活跃流是什么
在HTTP/2协议中,活跃流(Active Stream) 是指在一个TCP连接上同时处于打开状态、正在传输或等待处理的虚拟双向数据通道。它是HTTP/2实现多路复用(Multiplexing)的核心机制,彻底改变了HTTP/1.x时代的并发模型。以下是其核心要点及技术解析:
6.1🔍 一、活跃流的本质与定义
-
流(Stream)的概念
- HTTP/2将每个请求/响应分解为独立的二进制帧(Frame) ,通过流标识符(Stream ID) 归属到不同逻辑通道中。
- 活跃流特指处于
OPEN状态的流,即已创建但未关闭(未收到END_STREAM标志或RST_STREAM帧)的流。
-
关键特性
- 双向性:客户端和服务端可同时发送数据帧(如
HEADERS、DATA)。 - 并发性:单TCP连接上可并行存在多个活跃流,帧按流ID分组处理,互不阻塞。
- 有序性:同一流内的帧必须按顺序处理,但不同流的帧可交错传输。
- 双向性:客户端和服务端可同时发送数据帧(如
⚙️ 6.2、活跃流的协议层约束
1. 数量限制机制
-
SETTINGS_MAX_CONCURRENT_STREAMS参数: 连接建立时,客户端和服务端通过SETTINGS帧协商最大活跃流数量4710。-
RFC 7540建议值:不小于10078。
-
典型默认值:
环境 默认值 可调整范围 Chrome/Firefox 100~250 动态调整 Nginx 128 可调至1000+ Node.js 100 依赖底层库 610
-
2. 流标识符管理
- Stream ID为31位整数(范围1~2³¹-1,约21亿),按奇偶分配(客户端奇数/服务端偶数)。
- 不可复用:流关闭后其ID永久占用,耗尽需重建连接。
🧩 3、活跃流的状态与生命周期
- OPEN状态:流正在传输数据(如
DATA帧),受流量控制窗口约束。 - 阻塞场景:若流窗口耗尽(默认64KB)或达到
SETTINGS_MAX_CONCURRENT_STREAMS上限,新流需排队
⚖️ 4、流量控制对活跃流的影响
HTTP/2在应用层实现流控(Flow Control),解决多流协同问题:
-
窗口机制
- 每个活跃流有独立接收窗口(默认65,535字节),发送
DATA帧后窗口减小。 - 接收方通过
WINDOW_UPDATE帧动态扩窗(最大2³²-1字节)。
- 每个活跃流有独立接收窗口(默认65,535字节),发送
-
阻塞逻辑
- 若流窗口=0,其
DATA帧发送被暂停,但其他窗口未耗尽的流仍可传输。 - 连接级窗口(Stream ID=0)限制所有流总和。
- 若流窗口=0,其