HTTP/2核心特性详解
约 1712 字大约 6 分钟
http2protocol
2025-07-03
概述
HTTP/2(RFC 7540)是HTTP协议的第二个主要版本,于2015年正式发布。它在保持HTTP语义不变的前提下,通过二进制分帧、多路复用等机制大幅提升了传输效率,解决了HTTP/1.1的队头阻塞(Head-of-Line Blocking)等核心问题。
HTTP/1.1 的瓶颈
HTTP/1.1的应对措施(hack手段):
- 域名分片(domain sharding)
- 资源合并(CSS/JS bundling)
- 雪碧图(CSS sprites)
- 资源内联(data URI)
HTTP/2从根本上解决了这些问题,使这些hack手段不再必要。
二进制分帧层(Binary Framing Layer)
HTTP/2在应用层(HTTP)和传输层(TCP)之间引入了一个二进制分帧层。所有通信都被分割为更小的帧(frame),使用二进制编码。
帧格式:
+-----------------------------------------------+
| Length (24 bits) |
+---------------+---------------+---------------+
| Type (8) | Flags (8) |
+-+-------------+---------------+---------------+
|R| Stream Identifier (31 bits) |
+=+=============+===============================+
| Frame Payload ... |
+-----------------------------------------------+帧类型:
| Type | 名称 | 说明 |
|---|---|---|
| 0x0 | DATA | 传输请求/响应体 |
| 0x1 | HEADERS | 传输HTTP头部 |
| 0x2 | PRIORITY | 指定流优先级 |
| 0x3 | RST_STREAM | 终止流 |
| 0x4 | SETTINGS | 连接配置参数 |
| 0x5 | PUSH_PROMISE | 服务器推送预告 |
| 0x6 | PING | 连接探活 |
| 0x7 | GOAWAY | 优雅关闭连接 |
| 0x8 | WINDOW_UPDATE | 流量控制窗口更新 |
| 0x9 | CONTINUATION | HEADERS的延续帧 |
多路复用(Multiplexing)
HTTP/2在单个TCP连接上可以并发传输多个请求和响应,各请求/响应通过流(stream)区分。
关键概念:
- 连接(Connection): 一个TCP连接,承载多个流
- 流(Stream): 双向字节流,承载一个完整的请求/响应
- 帧(Frame): 通信的最小单位,不同流的帧可以交错
- 客户端发起的流使用奇数ID,服务端发起的使用偶数ID
头部压缩(HPACK)
HTTP头部通常包含大量重复信息(Cookie、User-Agent等),HPACK通过静态表、动态表和Huffman编码压缩头部。
静态表示例(前几条):
| Index | Header Name | Header Value |
|---|---|---|
| 1 | :authority | |
| 2 | :method | GET |
| 3 | :method | POST |
| 4 | :path | / |
| 5 | :path | /index.html |
| 6 | :scheme | http |
| 7 | :scheme | https |
| 8 | :status | 200 |
压缩效果:
第一次请求头部大小: ~800 bytes
第二次请求头部大小: ~20 bytes(大部分字段在动态表中命中)
压缩率: 95%+服务器推送(Server Push)
服务端可以主动推送客户端可能需要的资源,无需等待客户端请求。
Nginx配置Server Push:
server {
listen 443 ssl http2;
server_name example.com;
location = /index.html {
http2_push /css/style.css;
http2_push /js/app.js;
http2_push /img/logo.png;
}
}注意: Server Push在实践中效果参差不齐,Chrome已在104版本移除支持。103 Early Hints是更好的替代方案。
流优先级与依赖(Stream Prioritization)
客户端可以指定流的优先级和依赖关系,帮助服务端合理分配资源。
依赖树表示:Stream 3、5、7都依赖Stream 1,只有Stream 1完成后才处理它们。同级间按权重分配带宽。
流量控制(Flow Control)
HTTP/2在TCP流量控制之上实现了应用层的流量控制,细粒度到每个流。
特点:
- 基于窗口更新帧(WINDOW_UPDATE)
- 连接级别和流级别的双重控制
- 初始窗口大小: 65535 字节
- 只对DATA帧生效(HEADERS帧不受限)
- 接收方可以动态调整窗口
流量控制流程:
1. 初始窗口 = 65535
2. 发送DATA帧,窗口减小
3. 接收方处理数据后发送WINDOW_UPDATE
4. 发送方窗口恢复,继续发送h2 vs h2c
- h2: 基于TLS的HTTP/2(浏览器只支持这种)
- h2c: 明文HTTP/2(不使用TLS)
# h2c升级过程(HTTP Upgrade)
GET / HTTP/1.1
Host: example.com
Connection: Upgrade, HTTP2-Settings
Upgrade: h2c
HTTP2-Settings: <base64编码的SETTINGS帧>
# 服务端同意升级
HTTP/1.1 101 Switching Protocols
Connection: Upgrade
Upgrade: h2cALPN协商
Application-Layer Protocol Negotiation在TLS握手阶段协商使用HTTP/2:
# OpenSSL检查服务器ALPN支持
openssl s_client -alpn h2 -connect example.com:443
# 输出中查找: ALPN protocol: h2
# curl查看协议版本
curl -v --http2 https://example.com 2>&1 | grep "< HTTP/"
# < HTTP/2 200Nginx HTTP/2配置
server {
listen 443 ssl http2;
server_name example.com;
ssl_certificate /etc/ssl/certs/example.com.pem;
ssl_certificate_key /etc/ssl/private/example.com.key;
# HTTP/2相关调优
http2_max_concurrent_streams 128; # 最大并发流数
http2_max_field_size 8k; # 头部字段最大大小
http2_max_header_size 32k; # 头部总大小限制
http2_chunk_size 8k; # DATA帧大小
# 确保TLS配置兼容HTTP/2
ssl_protocols TLSv1.2 TLSv1.3;
ssl_ciphers ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256;
ssl_prefer_server_ciphers on;
}HTTP/2 vs HTTP/1.1 对比
| 特性 | HTTP/1.1 | HTTP/2 |
|---|---|---|
| 编码格式 | 文本 | 二进制 |
| 多路复用 | 不支持 | 支持 |
| 头部压缩 | 不支持 | HPACK |
| 服务器推送 | 不支持 | 支持 |
| 流优先级 | 不支持 | 支持 |
| 连接数 | 多个(6-8/域名) | 1个 |
| 队头阻塞 | HTTP层+TCP层 | 仅TCP层 |
局限性
HTTP/2仍然基于TCP,存在TCP层面的队头阻塞:一个TCP包丢失会阻塞该连接上所有流的数据。这也是HTTP/3选择基于UDP的QUIC协议的原因。
总结
HTTP/2通过二进制分帧和多路复用从根本上解决了HTTP/1.1的效率问题。在实际部署中,启用HTTP/2通常只需配置Web服务器和TLS证书,对应用层代码透明。但需要注意HTTP/2在TCP层面仍存在队头阻塞,这一问题在HTTP/3(QUIC)中得到了最终解决。
贡献者
更新日志
9f6c2-feat: organize wiki content and refresh site setup于