HTTP 协议版本演进
HTTP 1.0、1.1、2.0、3.0 的主要区别与特性对比
问题
HTTP 1.0、1.1、2.0、3.0 (QUIC) 各有什么特点?它们是如何演进的?
解答
HTTP 1.0
最早的 HTTP 版本,特点是短连接:
客户端 服务器
|---- TCP 握手 ---->|
|---- 请求 1 ------>|
|<--- 响应 1 -------|
|---- TCP 挥手 ---->|
|---- TCP 握手 ---->| (新连接)
|---- 请求 2 ------>|
|<--- 响应 2 -------|
|---- TCP 挥手 ---->|
每次请求都要建立新的 TCP 连接,开销大。
HTTP 1.1
引入长连接,默认 Connection: keep-alive:
客户端 服务器
|---- TCP 握手 ---->|
|---- 请求 1 ------>|
|<--- 响应 1 -------|
|---- 请求 2 ------>| (复用连接)
|<--- 响应 2 -------|
|---- 请求 3 ------>|
|<--- 响应 3 -------|
|---- TCP 挥手 ---->|
问题:队头阻塞(Head-of-Line Blocking)。请求必须按顺序响应,前面的请求慢会阻塞后面的。
// 浏览器通常对同一域名开 6 个 TCP 连接来缓解
// 这也是为什么有域名分片的优化手段
HTTP 2.0
基于 Google 的 SPDY 协议,引入三大特性:
1. 多路复用
一个 TCP 连接上可以并行传输多个请求/响应,互不阻塞:
┌─────────────────────────────────────┐
│ 单个 TCP 连接 │
├─────────────────────────────────────┤
│ Stream 1: 请求A ──────> 响应A │
│ Stream 2: 请求B ──> 响应B │
│ Stream 3: 请求C ────────────> 响应C │
└─────────────────────────────────────┘
2. 头部压缩 (HPACK)
# HTTP 1.1 每次请求都发送完整头部
GET /page1 HTTP/1.1
Host: example.com
User-Agent: Mozilla/5.0...
Accept: text/html...
Cookie: session=abc123...
GET /page2 HTTP/1.1
Host: example.com # 重复
User-Agent: Mozilla/5.0... # 重复
Accept: text/html... # 重复
Cookie: session=abc123... # 重复
# HTTP 2.0 使用索引表 + 哈夫曼编码
# 重复的头部只发送索引号,压缩率可达 90%+
3. Server Push
服务器可以主动推送资源:
// 客户端请求 index.html
// 服务器可以主动推送 style.css 和 app.js
// Node.js 示例 (http2 模块)
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) => {
if (headers[':path'] === '/index.html') {
// 主动推送 CSS
stream.pushStream({ ':path': '/style.css' }, (err, pushStream) => {
pushStream.respond({ ':status': 200, 'content-type': 'text/css' });
pushStream.end('body { color: red; }');
});
// 响应 HTML
stream.respond({ ':status': 200, 'content-type': 'text/html' });
stream.end('<html>...</html>');
}
});
HTTP 2.0 的问题:TCP 层的队头阻塞。丢包时整个 TCP 连接都要等待重传。
HTTP 3.0 (QUIC)
基于 UDP 的 QUIC 协议,彻底解决队头阻塞:
┌─────────────────────────────────────┐
│ QUIC │
├─────────────────────────────────────┤
│ Stream 1: ████░████ (丢包重传) │
│ Stream 2: ██████████ (不受影响) │
│ Stream 3: ██████████ (不受影响) │
└─────────────────────────────────────┘
主要改进
特性 HTTP/2 (TCP) HTTP/3 (QUIC)
─────────────────────────────────────────────────────
连接建立 TCP + TLS (2-3 RTT) 0-1 RTT
队头阻塞 TCP 层存在 完全解决
连接迁移 IP 变化需重连 支持 (Connection ID)
加密 TLS 可选 内置 TLS 1.3
版本对比总结
| 特性 | 1.0 | 1.1 | 2.0 | 3.0 |
|---|---|---|---|---|
| 连接方式 | 短连接 | 长连接 | 多路复用 | 多路复用 |
| 传输协议 | TCP | TCP | TCP | QUIC (UDP) |
| 头部压缩 | ❌ | ❌ | HPACK | QPACK |
| Server Push | ❌ | ❌ | ✅ | ✅ |
| 队头阻塞 | 严重 | HTTP 层 | TCP 层 | 无 |
关键点
- HTTP 1.1 的长连接复用 TCP 连接,但请求仍需排队
- HTTP 2.0 的多路复用允许并行请求,但 TCP 丢包会阻塞所有流
- HTTP 2.0 的 HPACK 头部压缩通过静态表 + 动态表 + 哈夫曼编码实现
- HTTP 3.0 用 QUIC 替换 TCP,每个流独立,丢包不影响其他流
- QUIC 内置 TLS 1.3,支持 0-RTT 快速连接和连接迁移
目录