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.01.12.03.0
连接方式短连接长连接多路复用多路复用
传输协议TCPTCPTCPQUIC (UDP)
头部压缩HPACKQPACK
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 快速连接和连接迁移