HTTP 版本演进
HTTP(HyperText Transfer Protocol,超文本传输协议)是互联网上应用最广泛的协议之一。从 1991 年诞生至今,HTTP 经历了多次重大升级,每一次升级都是为了解决上一版本的痛点。本文将带你了解 HTTP 从 1.0 到 3.0 的演进历程。
HTTP/1.0:一次请求一次连接
背景
HTTP/1.0 于 1996 年发布(RFC 1945),是第一个被广泛使用的 HTTP 版本。在此之前的 HTTP/0.9 只能传输 HTML 文本,功能非常有限。
核心特性
- 请求/响应模型:客户端发送请求,服务器返回响应
- 支持多种内容类型:通过
Content-Type头部支持图片、视频等多媒体 - 状态码:引入 200、404、500 等状态码
- 请求头和响应头:可以传递元信息
主要问题:短连接
HTTP/1.0 默认使用短连接,每次请求都需要经历完整的 TCP 三次握手和四次挥手:
sequenceDiagram
participant 客户端
participant 服务器
Note over 客户端,服务器: 第一次请求
客户端->>服务器: TCP 三次握手
客户端->>服务器: GET /index.html
服务器->>客户端: 200 OK + HTML内容
客户端->>服务器: TCP 四次挥手
Note over 客户端,服务器: 第二次请求
客户端->>服务器: TCP 三次握手
客户端->>服务器: GET /style.css
服务器->>客户端: 200 OK + CSS内容
客户端->>服务器: TCP 四次挥手
请求示例
GET /index.html HTTP/1.0
Host: www.example.com
User-Agent: Mozilla/5.0
Accept: text/html
HTTP/1.0 200 OK
Content-Type: text/html
Content-Length: 1234
<!DOCTYPE html>
<html>...
痛点总结
| 问题 | 影响 |
|---|---|
| 每次请求都要建立新连接 | 延迟高,TCP 握手开销大 |
| 无法复用连接 | 服务器资源消耗大 |
| 队头阻塞 | 必须等上一个请求完成才能发下一个 |
HTTP/1.1:持久连接与管道化
背景
HTTP/1.1 于 1997 年发布(RFC 2068,后更新为 RFC 2616),是目前仍被广泛使用的版本。它主要解决了 HTTP/1.0 的连接效率问题。
核心改进
1. 持久连接(Keep-Alive)
HTTP/1.1 默认启用持久连接,一个 TCP 连接可以发送多个请求:
sequenceDiagram
participant 客户端
participant 服务器
客户端->>服务器: TCP 三次握手
Note over 客户端,服务器: 复用同一连接
客户端->>服务器: GET /index.html
服务器->>客户端: 200 OK + HTML
客户端->>服务器: GET /style.css
服务器->>客户端: 200 OK + CSS
客户端->>服务器: GET /script.js
服务器->>客户端: 200 OK + JS
客户端->>服务器: TCP 四次挥手
2. 管道化(Pipelining)
客户端可以不等响应就连续发送多个请求:
sequenceDiagram
participant 客户端
participant 服务器
客户端->>服务器: GET /a.html
客户端->>服务器: GET /b.css
客户端->>服务器: GET /c.js
服务器->>客户端: 响应 a.html
服务器->>客户端: 响应 b.css
服务器->>客户端: 响应 c.js
⚠️ 注意:管道化要求响应必须按请求顺序返回,实际应用中因为队头阻塞问题,大多数浏览器默认禁用了管道化。
3. 分块传输编码(Chunked Transfer)
服务器可以边生成边发送内容,不需要预先知道内容总长度:
HTTP/1.1 200 OK
Transfer-Encoding: chunked
7\r\n
Hello, \r\n
6\r\n
World!\r\n
0\r\n
\r\n
4. 其他改进
- Host 头部:支持虚拟主机,一个 IP 可以托管多个网站
- 缓存控制:引入
Cache-Control、ETag等缓存机制 - 断点续传:通过
Range头部支持部分内容请求
请求示例
GET /api/users HTTP/1.1
Host: api.example.com
Connection: keep-alive
Accept: application/json
Cache-Control: no-cache
仍存在的问题
| 问题 | 说明 |
|---|---|
| 队头阻塞(HOL Blocking) | 响应必须按顺序返回,前面的慢会阻塞后面的 |
| 头部冗余 | 每次请求都要发送完整的头部,很多是重复的 |
| 文本协议 | 解析效率不如二进制协议 |
| 单向通信 | 服务器不能主动推送数据 |
HTTP/2:多路复用与二进制分帧
背景
HTTP/2 于 2015 年发布(RFC 7540),基于 Google 的 SPDY 协议演化而来。它在保持 HTTP/1.1 语义的同时,彻底改变了数据传输方式。
核心改进
1. 二进制分帧层
HTTP/2 在应用层和传输层之间引入了二进制分帧层,将所有通信分割成更小的帧:
+------------------+
| HTTP 语义层 | ← 请求方法、头部、状态码(与 HTTP/1.1 兼容)
+------------------+
| 二进制分帧层 | ← HTTP/2 新增
+------------------+
| TLS | ← 加密(通常必需)
+------------------+
| TCP |
+------------------+
2. 多路复用(Multiplexing)
在一个 TCP 连接上可以并行传输多个请求和响应,彻底解决了应用层的队头阻塞:
sequenceDiagram
participant 客户端
participant 服务器
Note over 客户端,服务器: 单一 TCP 连接,并行传输
客户端->>服务器: Stream 1: GET /index.html
客户端->>服务器: Stream 3: GET /style.css
客户端->>服务器: Stream 5: GET /script.js
服务器->>客户端: Stream 3: CSS 帧 1
服务器->>客户端: Stream 1: HTML 帧 1
服务器->>客户端: Stream 5: JS 帧 1
服务器->>客户端: Stream 1: HTML 帧 2
服务器->>客户端: Stream 3: CSS 帧 2
服务器->>客户端: Stream 5: JS 帧 2
关键概念:
- 流(Stream):一个独立的双向数据流,承载一对请求/响应
- 帧(Frame):HTTP/2 通信的最小单位,每个帧属于某个流
3. 头部压缩(HPACK)
使用 HPACK 算法压缩头部,维护一个头部字段表,只发送差异部分:
第一次请求头部(完整发送):
:method: GET
:path: /index.html
:authority: example.com
user-agent: Mozilla/5.0
accept: text/html
第二次请求头部(只发送差异):
:path: /style.css ← 只有路径变了
accept: text/css ← 只有 accept 变了
压缩效果:头部大小可减少 85%-90%。
4. 服务器推送(Server Push)
服务器可以主动推送客户端可能需要的资源:
sequenceDiagram
participant 客户端
participant 服务器
客户端->>服务器: GET /index.html
服务器->>客户端: PUSH_PROMISE: /style.css
服务器->>客户端: PUSH_PROMISE: /script.js
服务器->>客户端: 响应 /index.html
服务器->>客户端: 推送 /style.css
服务器->>客户端: 推送 /script.js
5. 流优先级
客户端可以指定流的优先级,让重要资源(如 CSS)优先传输。
使用示例
使用 curl 查看 HTTP/2 连接:
# 查看 HTTP/2 请求详情
curl -v --http2 https://www.google.com
# 输出会显示:
# * Using HTTP2, server supports multi-use
# * Connection state changed (HTTP/2 confirmed)
# > GET / HTTP/2
仍存在的问题
| 问题 | 说明 |
|---|---|
| TCP 队头阻塞 | 虽然解决了 HTTP 层的队头阻塞,但 TCP 层仍存在 |
| TCP 握手延迟 | 仍需要 TCP 三次握手 + TLS 握手 |
| 连接迁移困难 | 切换网络(如 WiFi 到 4G)需要重新建立连接 |
💡 TCP 队头阻塞:TCP 保证数据按序到达,如果一个数据包丢失,后续所有数据包都要等待重传,即使它们属于不同的 HTTP 流。
HTTP/3:基于 QUIC 的革新
背景
HTTP/3 于 2022 年正式发布(RFC 9114),最大的变化是将传输层从 TCP 切换到 QUIC(Quick UDP Internet Connections)。QUIC 最初由 Google 开发,后被 IETF 标准化。
为什么需要 HTTP/3?
HTTP/2 的 TCP 队头阻塞问题无法在应用层解决,必须改变传输层协议:
HTTP/2 的问题:
┌─────────────────────────────────────┐
│ Stream 1 │ Stream 2 │ Stream 3 │ ← HTTP/2 多路复用
├─────────────────────────────────────┤
│ TCP 连接 │ ← 一个包丢失,全部阻塞
└─────────────────────────────────────┘
HTTP/3 的解决方案:
┌─────────────────────────────────────┐
│ Stream 1 │ Stream 2 │ Stream 3 │ ← QUIC 流
├────────────┼────────────┼───────────┤
│ 独立传输 │ 独立传输 │ 独立传输 │ ← 互不影响
└─────────────────────────────────────┘
核心特性
1. 基于 UDP + QUIC
QUIC 在 UDP 之上实现了可靠传输,同时避免了 TCP 的队头阻塞:
HTTP/3 协议栈:
+------------------+
| HTTP/3 |
+------------------+
| QUIC | ← 包含流控制、可靠传输、加密
+------------------+
| UDP |
+------------------+
| IP |
+------------------+
2. 0-RTT 连接建立
QUIC 将传输层握手和 TLS 握手合并,首次连接只需 1-RTT,重连可以实现 0-RTT:
sequenceDiagram
participant 客户端
participant 服务器
Note over 客户端,服务器: TCP + TLS 1.3(2-RTT)
客户端->>服务器: TCP SYN
服务器->>客户端: TCP SYN-ACK
客户端->>服务器: TCP ACK + TLS ClientHello
服务器->>客户端: TLS ServerHello + 证书
客户端->>服务器: TLS Finished + HTTP 请求
Note over 客户端,服务器: QUIC(1-RTT,重连 0-RTT)
客户端->>服务器: QUIC Initial + TLS ClientHello
服务器->>客户端: QUIC Handshake + TLS 完成
客户端->>服务器: HTTP/3 请求(可与握手同时)
3. 连接迁移
QUIC 使用连接 ID 而非 IP+端口 来标识连接,网络切换时无需重新建立连接:
场景:用户从 WiFi 切换到 4G
TCP/HTTP2:
WiFi 断开 → 连接中断 → 重新三次握手 → 重新 TLS 握手 → 继续传输
QUIC/HTTP3:
WiFi 断开 → IP 变化 → 连接 ID 不变 → 无缝继续传输
4. 改进的拥塞控制
QUIC 在用户空间实现拥塞控制,可以更快地迭代和优化算法,不受操作系统内核限制。
浏览器支持
目前主流浏览器和网站都已支持 HTTP/3:
# 检查网站是否支持 HTTP/3
curl -I --http3 https://www.cloudflare.com
# 或使用在线工具
# https://http3check.net
注意事项
- HTTP/3 需要服务器和客户端都支持
- 某些网络环境可能屏蔽 UDP,会自动降级到 HTTP/2
- 调试工具支持还在完善中
版本对比总结
| 特性 | HTTP/1.0 | HTTP/1.1 | HTTP/2 | HTTP/3 |
|---|---|---|---|---|
| 发布年份 | 1996 | 1997 | 2015 | 2022 |
| 传输层 | TCP | TCP | TCP | QUIC (UDP) |
| 连接方式 | 短连接 | 持久连接 | 多路复用 | 多路复用 |
| 队头阻塞 | 有 | 有 | HTTP层无,TCP层有 | 无 |
| 头部格式 | 文本 | 文本 | 二进制 + HPACK压缩 | 二进制 + QPACK压缩 |
| 服务器推送 | 不支持 | 不支持 | 支持 | 支持 |
| 加密 | 可选 | 可选 | 事实上必需 | 强制 |
| 连接建立 | 1-RTT | 1-RTT | 2-RTT (TCP+TLS) | 1-RTT / 0-RTT |
演进路线图
graph LR
A[HTTP/1.0<br/>短连接] -->|解决连接复用| B[HTTP/1.1<br/>持久连接]
B -->|解决队头阻塞| C[HTTP/2<br/>多路复用]
C -->|解决TCP队头阻塞| D[HTTP/3<br/>QUIC]