HTTP/2 中如何实现首部压缩?
HTTP/2 使用 HPACK 算法通过静态和动态表以及 Huffman 编码来压缩请求和响应头,减少数据传输量。
HTTP/2 中的首部压缩通过使用 HPACK 算法实现,以高效减少每次请求和响应的头部数据大小,从而提高性能并降低带宽占用。
其实现原理包括以下主要机制:
1. 表结构的组成与维护
HTTP/2 在客户端和服务器端维护两份共享的表来存储头部信息:
- 静态表:包含预定义的 61 个常⺡头部键值对(如
:method
、:path
、host
),这些由协议固定设置。 -
动态表:用于在连接生命周期内持续跟踪发送的头部数据,由双方渐进更新;任何新头部键值对会被追加或替换现有条目。
例如,如果首次请求发送
accept-language: zh-CN
,该条目即被缓存到动态表中;后续请求若使用相同值,只需发送索引号而不发送全字符串。
2. 编码和传输规则
- 索引编码:头部数据基于表索引来表示,常见步骤如下:
- 完整键值对存在于表中:发送单个0或1开头的标识帧及表索引。
- 头部值匹配但名称不匹配:使用增量编码发送差异化名称部分。
- 新增条目:添加到动态表尾部并进行编号更新。
-
Huffman编码:头部串值应用哈夫曼压缩进一步缩小体积;这使用预设的哈夫曼树,根据字符频率优化编码。
在 HTTP/2 Frame 结构中处理时:
Headers Frame: { Index (0-6 bits) : 表示键值的索引位置 Value Prefix Flags (2-3 bits) : 控制 Huffman 标志位 Dynamic Table Size Update : 动态更新指令 }
3. 工作流程示例
假设初次请求携带头部 accept: text/html; charset=utf-8
:
- 初始传输时需发全字符串并更新动态表。
- 下次请求若值相同(
accept: text/html; charset=utf-8
):客户端只发送动态表索引##2
(索引实例) - 如服务端收到差异值:将索引对应项替换为新数据并响应更新指令;该表生命周期与连接保持一致。
通过此机制,在连接中重复数据的传输开销接近零;尤其对像 cookie
一类高频变更首部,压缩增益可达50%以上。