Skip to content

题目

请详细说明 HTTP 1.1, 2.0, 3.0 的主要区别。

📝 标准答案

核心要点

  1. HTTP/1.1(1997年)

    • 持久连接(Keep-Alive)
    • 管道化(Pipelining)
    • 缓存控制
    • 问题:队头阻塞、连接数限制
  2. HTTP/2(2015年)

    • 二进制分帧
    • 多路复用
    • 头部压缩(HPACK)
    • 服务器推送
    • 基于 TCP
  3. HTTP/3(2022年)

    • 基于 QUIC(UDP)
    • 解决队头阻塞
    • 0-RTT 连接建立
    • 连接迁移
    • 更好的拥塞控制

详细说明

HTTP/1.1 的特点

1. 持久连接(Keep-Alive)

http
// HTTP/1.0:每个请求都需要建立新连接
请求1 → 建立连接 → 响应1 → 关闭连接
请求2 → 建立连接 → 响应2 → 关闭连接

// HTTP/1.1:复用连接
建立连接 → 请求1 → 响应1 → 请求2 → 响应2 → ... → 关闭连接

Connection: keep-alive
Keep-Alive: timeout=5, max=100

2. 管道化(Pipelining)

http
// 不等待响应,连续发送多个请求
请求1 → 请求2 → 请求3 → 响应1 → 响应2 → 响应3

// 问题:响应必须按顺序返回(队头阻塞)

3. HTTP/1.1 的问题

1. 队头阻塞(Head-of-Line Blocking)
   - 前面的请求阻塞后面的请求
   - 即使后面的请求已经处理完成

2. 连接数限制
   - 浏览器限制同域名并发连接数(通常 6 个)
   - 需要域名分片(Domain Sharding)

3. 头部冗余
   - 每个请求都携带完整的头部
   - 大量重复信息

4. 明文传输
   - 不安全,需要 HTTPS

🧠 深度理解

HTTP/2 的改进

1. 二进制分帧(Binary Framing)

HTTP/1.1:文本协议
GET /index.html HTTP/1.1
Host: example.com

HTTP/2:二进制协议
+-----------------------------------------------+
| Length (24) | Type (8) | Flags (8) | Stream ID (31) |
+-----------------------------------------------+
|                   Frame Payload                |
+-----------------------------------------------+

2. 多路复用(Multiplexing)

HTTP/1.1:
连接1 → 请求1 → 响应1
连接2 → 请求2 → 响应2
连接3 → 请求3 → 响应3

HTTP/2:
单个连接 → 请求1、请求2、请求3 → 响应1、响应2、响应3
(并行,不按顺序)

优势:
- 解决队头阻塞(应用层)
- 减少连接数
- 更好的资源利用

3. 头部压缩(HPACK)

javascript
// HTTP/1.1:每次都发送完整头部
GET /api/user HTTP/1.1
Host: example.com
User-Agent: Mozilla/5.0...
Accept: application/json
Cookie: session=abc123...

GET /api/posts HTTP/1.1
Host: example.com
User-Agent: Mozilla/5.0...  // 重复
Accept: application/json    // 重复
Cookie: session=abc123...   // 重复

// HTTP/2:使用索引表压缩
请求1:完整头部
请求2:[2] [3] [4]  // 引用索引,大幅减少数据量

4. 服务器推送(Server Push)

javascript
// 客户端请求 HTML
GET /index.html

// 服务器主动推送 CSS 和 JS
PUSH_PROMISE /style.css
PUSH_PROMISE /script.js

// 客户端收到 HTML 和推送的资源
// 无需额外请求

5. HTTP/2 的问题

TCP 层的队头阻塞:
- HTTP/2 解决了应用层的队头阻塞
- 但 TCP 层仍然存在队头阻塞
- 一个 TCP 包丢失,会阻塞整个连接

示例:
Stream 1: [包1] [包2] [包3]
Stream 2: [包4] [包5] [包6]

如果包2丢失:
- 包3、包4、包5、包6 都要等待包2重传
- 即使它们已经到达

HTTP/3 的革新

1. 基于 QUIC(Quick UDP Internet Connections)

HTTP/1.1 和 HTTP/2:
应用层 (HTTP) → 传输层 (TCP) → 网络层 (IP)

HTTP/3:
应用层 (HTTP/3) → 传输层 (QUIC/UDP) → 网络层 (IP)

QUIC 特点:
- 基于 UDP
- 内置 TLS 1.3
- 连接迁移
- 改进的拥塞控制

2. 解决队头阻塞

HTTP/2 (TCP):
Stream 1: [包1] [包2丢失] [包3]
Stream 2: [包4] [包5] [包6]
→ 包2丢失,所有流都阻塞

HTTP/3 (QUIC):
Stream 1: [包1] [包2丢失] [包3]
Stream 2: [包4] [包5] [包6]
→ 包2丢失,只阻塞 Stream 1,Stream 2 继续

3. 0-RTT 连接建立

HTTP/1.1 + TLS 1.2:
TCP 三次握手 (1 RTT) + TLS 握手 (2 RTT) = 3 RTT

HTTP/2 + TLS 1.3:
TCP 三次握手 (1 RTT) + TLS 握手 (1 RTT) = 2 RTT

HTTP/3 + QUIC:
QUIC 握手 (1 RTT)
或 0-RTT(使用之前的会话密钥)

4. 连接迁移

场景:手机从 WiFi 切换到 4G

HTTP/1.1 和 HTTP/2:
- IP 地址改变
- TCP 连接断开
- 需要重新建立连接

HTTP/3:
- 使用连接 ID 标识连接
- IP 地址改变不影响连接
- 无缝切换网络

版本对比表

特性HTTP/1.1HTTP/2HTTP/3
发布年份199720152022
传输协议TCPTCPQUIC (UDP)
数据格式文本二进制二进制
多路复用
头部压缩✅ (HPACK)✅ (QPACK)
服务器推送
队头阻塞✅ 应用层✅ TCP层
连接建立3 RTT2 RTT0-1 RTT
连接迁移

性能对比

页面加载时间(100个资源):

HTTP/1.1:
- 6个并发连接
- 每个连接串行加载
- 总时间:约 10 秒

HTTP/2:
- 单个连接
- 多路复用
- 总时间:约 3 秒

HTTP/3:
- 单个连接
- 多路复用 + 无队头阻塞
- 总时间:约 2 秒

常见误区

  1. 误区:HTTP/2 一定比 HTTP/1.1 快

    ❌ 不一定
    - 在丢包率高的网络环境下,HTTP/2 可能更慢
    - TCP 队头阻塞会影响所有流
    - HTTP/1.1 的多个连接可以并行重传
  2. 误区:HTTP/3 完全替代 HTTP/2

    ❌ 不是
    - HTTP/3 需要 UDP 支持
    - 部分网络环境阻止 UDP
    - 需要降级到 HTTP/2 或 HTTP/1.1
  3. 误区:HTTP/2 不需要域名分片

    ✅ 正确
    - HTTP/2 多路复用,单个连接即可
    - 域名分片反而会降低性能
    - 增加 DNS 查询和 TCP 连接开销

💡 面试回答技巧

🎯 一句话回答(快速版)

HTTP/1.1 有队头阻塞问题;HTTP/2 用多路复用解决应用层队头阻塞;HTTP/3 基于 QUIC(UDP)解决 TCP 层队头阻塞,支持 0-RTT 和连接迁移。

📣 口语化回答(推荐)

面试时可以这样回答:

"HTTP 协议经历了几个重要版本的演进。

HTTP/1.1 引入了持久连接,不用每次请求都重新建立 TCP 连接。但它有个问题叫队头阻塞,一个请求卡住了,后面的请求都得等着。虽然可以开多个 TCP 连接,但浏览器限制同域名最多 6 个。

HTTP/2 做了很大改进。最重要的是多路复用,一个 TCP 连接上可以同时发送多个请求,互不阻塞。还有二进制分帧,把数据拆成帧传输;头部压缩,用 HPACK 算法压缩重复的头部;服务器推送,可以主动推送资源给客户端。

但 HTTP/2 还是基于 TCP,TCP 层的队头阻塞没解决。一个包丢了,整个连接都要等重传。

HTTP/3 改用 QUIC 协议,基于 UDP。QUIC 在应用层实现了可靠传输,一个流丢包不影响其他流。还支持 0-RTT,之前连接过的服务器可以直接发数据不用握手。还有连接迁移,切换网络 IP 变了连接不会断。

现在 HTTP/2 已经很普及了,HTTP/3 也在逐步推广。"

推荐回答顺序

  1. 先说 HTTP/1.1

    • "持久连接、管道化"
    • "问题:队头阻塞、连接数限制"
  2. 再说 HTTP/2

    • "二进制分帧、多路复用"
    • "头部压缩、服务器推送"
    • "解决应用层队头阻塞"
  3. 然后说 HTTP/3

    • "基于 QUIC(UDP)"
    • "解决 TCP 层队头阻塞"
    • "0-RTT、连接迁移"
  4. 最后说应用

    • "HTTP/2 已广泛应用"
    • "HTTP/3 逐步普及"
    • "需要考虑兼容性"

重点强调

  • 多路复用的优势
  • 队头阻塞的层次
  • QUIC 的创新
  • 性能提升的原因

可能的追问

Q1: 如何升级到 HTTP/2?

A:

  1. 服务器支持 HTTP/2
  2. 启用 HTTPS(HTTP/2 要求)
  3. 配置 ALPN(Application-Layer Protocol Negotiation)
  4. 浏览器自动协商使用 HTTP/2

Q2: HTTP/2 的服务器推送有什么问题?

A:

  • 可能推送不需要的资源
  • 浪费带宽
  • 缓存管理复杂
  • HTTP/3 改进了推送机制

Q3: 为什么 HTTP/3 使用 UDP?

A:

  • TCP 协议固化在操作系统内核
  • 难以修改和升级
  • UDP 更灵活,可以在用户空间实现
  • QUIC 在 UDP 之上实现了可靠传输

Q4: 如何检测网站使用的 HTTP 版本?

A:

javascript
// Chrome DevTools
// Network → Protocol 列

// 或使用 JavaScript
fetch('https://example.com')
  .then(res => {
    console.log(res.headers.get('alt-svc'));
    // h3=":443" 表示支持 HTTP/3
  });

💻 代码示例

HTTP/2 服务器配置

javascript
// Node.js HTTP/2 服务器
const http2 = require('http2');
const fs = require('fs');

const server = http2.createSecureServer({
  key: fs.readFileSync('server.key'),
  cert: fs.readFileSync('server.crt')
});

server.on('stream', (stream, headers) => {
  // 服务器推送
  stream.pushStream({ ':path': '/style.css' }, (err, pushStream) => {
    if (err) throw err;
    pushStream.respond({ ':status': 200 });
    pushStream.end(fs.readFileSync('style.css'));
  });
  
  // 响应主请求
  stream.respond({
    'content-type': 'text/html',
    ':status': 200
  });
  stream.end('<html><body>Hello HTTP/2</body></html>');
});

server.listen(443);

检测 HTTP 版本

javascript
// 检测浏览器支持的 HTTP 版本
async function detectHTTPVersion(url) {
  try {
    const response = await fetch(url);
    const protocol = response.headers.get('alt-svc');
    
    if (protocol && protocol.includes('h3')) {
      return 'HTTP/3';
    } else if (window.chrome && chrome.loadTimes) {
      const timing = chrome.loadTimes();
      if (timing.npnNegotiatedProtocol === 'h2') {
        return 'HTTP/2';
      }
    }
    
    return 'HTTP/1.1';
  } catch (error) {
    console.error('Error:', error);
    return 'Unknown';
  }
}

// 使用
detectHTTPVersion('https://example.com')
  .then(version => console.log('HTTP Version:', version));

🔗 相关知识点

📚 参考资料

基于 MIT 许可发布 | 隐私政策