HTTP/2 学习

一、数据传输单元-帧

1. 通用定义(网络层面)

帧 = 一段固定格式、独立完整的一小块数据 网络传输时,不会把一整份超大文件一次性丢出去,而是切碎成一小块一小块,每一小块就是一帧。

举生活类比

你要寄一整套百科全书(完整请求 / 响应数据):

  1. 把每一页单独装进标准快递小盒子();
  2. 每个盒子外面贴统一格式标签(帧头:长度、类型、编号);
  3. 一堆盒子混杂装车运输(多路复用,不同请求的盒子混在一起传);
  4. 收件人看每个盒子标签,区分这一页属于哪本书、是什么内容。

这里的标准小盒子 = HTTP2 的帧

2. HTTP2 里帧的专属特点

  1. 最小传输单元 HTTP2 所有数据(请求头、响应体、推送预告、心跳、流量控制)全部拆成帧传输,不存在完整报文直接发送。
  2. 每个帧都有固定 9 字节头部标签 标签写清楚 4 件关键信息:
  • 这块帧的数据有多长;
  • 这是什么类型的帧(HEADERS 头帧、DATA 数据帧、PUSH_PROMISE 推送帧等);
  • 标记位(是否是数据结尾);
  • Stream 流 ID:这块数据属于哪一次请求。
  1. 不同请求的帧可以交错乱序传输 页面 A 的 DATA 帧、图片 B 的 HEADERS 帧、推送资源的 PUSH_PROMISE 帧,可以混在一起传输,依靠帧里的流 ID 区分归属,解决 HTTP1.1 排队阻塞问题。

3. 对比 HTTP/1.1 彻底分清

  • HTTP/1.1:一整个请求 / 响应是一整段连续文本字符串,没有切割成小块,只能完整排队传输;
  • HTTP/2:把请求、响应切碎成无数独立帧小块,带标签、可乱序穿插传输。

二、HPACK 压缩(静态表、哈夫曼、动态表)

前置背景

HTTP/1.1 每次请求重复带一堆重复头: Host: xxxUser-AgentAccept-EncodingCookie 每次完整传输,流量浪费巨大;HPACK 就是专门压缩请求头的算法,由三部分配合工作。

1、静态表(内置字典,客户端 + 服务器都自带)

大白话解释

官方提前写死一张固定表格 ,表里存了 61 组全世界网站通用的 Header 键值对,每个条目分配一个固定数字编号。

举几个表里现成的条目:

  • 编号 2:GET /
  • 编号 3:POST /
  • 编号 8:Host:
  • 编号 12:User-Agent:
  • 编号 16:Accept-Encoding: gzip, deflate

怎么用?

客户端要发送 Host: douyin.com

  1. Host: 在静态表里编号 8;
  2. 只需要传给服务器一个数字 8 ,不用完整传输 Host: 这一串文字;
  3. 服务器自带一模一样的静态表,看到数字 8,自动还原出 Host:

优点

最省流量,只用一个数字代替一长串字符串。


2、哈夫曼 Huffman 编码(压缩字符串正文)

适用场景

静态表里没有 的内容,比如域名 douyin.com、Cookie 字符串、自定义 Header 值。 这些文字没法用数字查表,就用哈夫曼压缩。

通俗原理

统计高频出现的字符,给常用字符分配更短的二进制 01 编码 ,冷门字符用长编码。 举例: omc 在域名里经常出现,只用 2 位二进制表示; 少见符号比如 #& 用长编码。

作用

把长字符串压缩成很短的二进制流,大幅减少字节数。


3、动态表(双方实时共享的临时字典)

大白话解释

静态表只有 61 条固定通用 header,我们自己业务的专属头(比如 token: xxx、自定义埋点字段)不在静态表里。 动态表是客户端和服务器通信过程中,动态新增、实时同步的一张临时表格。

工作流程举例

  1. 客户端第一次发请求,携带头 token: abc123
    • 静态表里没有这条,先用哈夫曼压缩字符串发给服务器;
    • 发送完成后,客户端、服务器同时把 token: abc123 存入各自的动态表,分配一个临时数字编号;
  2. 第二次请求再带 token: abc123
    • 不用传完整字符串,只传动态表里的临时数字编号;
    • 服务器查表直接还原,流量极致压缩。

补充特性

  1. 动态表有最大容量限制,满了之后会淘汰最久没用的旧条目;
  2. 动态表只在当前 TCP 连接生效,连接断开直接清空,下一条连接重新生成。

三者配合完整流程(举真实例子)

客户端发送请求头:Host: douyin.com

  1. Host: 键存在静态表,只传数字 8;
  2. douyin.com 不在静态表,用哈夫曼编码压缩后传输;
  3. 本次完整键值对存入动态表
  4. 下次再发 Host: douyin.com,直接传动态表的数字编号,不需要传输任何文字。

三、并发传输

1. 先看反面:HTTP/1.1 做不到

HTTP/1.1 一条 TCP 连接串行处理请求,分两种情况:

(1)短连接(默认老模式)

发请求 1 → 等完整响应 1 → 关闭 TCP;再新建 TCP 发请求 2。

(2)长连接 Keep-Alive

复用一条 TCP,但必须排队 : 先发完整请求 1 → 服务器完整返回响应 1; 才能发完整请求 2 → 等响应 2。 队头阻塞问题:如果请求 1(大图片)传输很慢,后面请求 2、3 就算很小,也必须排队等待,无法并行传输。

简单总结:HTTP/1.1 一条 TCP 同一时间只能处理1 个请求

2. HTTP/2 单 TCP 并发多请求是什么意思

浏览器和服务器之间只建立 1 条 TCP 长连接 ,所有页面资源(html、css、图片、接口、视频分片)全部通过这唯一一条 TCP 传输,且多个请求同时穿插收发,不用排队等待

实现核心:二进制帧 + Stream 流 ID

  1. HTTP/2 把每个请求拆成大量二进制小帧
  2. 给每一个独立请求分配唯一 Stream ID
  3. 一条 TCP 管道里,不同 Stream 的帧可以乱序、交错发送

举直观例子

页面同时需要 3 个资源:页面 HTML(流 1)、头像图片(流 2)、接口数据(流 3) 传输顺序可以是: 流 1 头部帧 → 流 2 头部帧 → 流 2 数据帧 → 流 1 数据帧 → 流 3 头部帧 → 流 3 数据帧 服务器收到帧后,根据 Stream ID 区分属于哪个请求,分别组装成完整响应返回。

哪怕流 1 的大文件传输卡顿,流 2、流 3 的小数据帧依旧可以正常收发,不会被前面慢请求阻塞,这就是多路复用。

疑问 1:并发处理多个请求,确实需要服务器支持并行逻辑,但光靠服务器并行处理,解决不了 TCP 管道里排队阻塞的问题

你设想的场景: 不用二进制帧,依旧用 HTTP1.1 文本报文;服务器多线程并行解析多个请求。 这里有个致命缺陷:TCP 是字节流,无边界。 举例子:客户端一次性把 2 个完整文本请求塞给 TCP 缓冲区:

复制代码
GET /a HTTP/1.1\r\nxxx\r\n\r\n
GET /b HTTP/1.1\r\nxxx\r\n\r\n

数据在 TCP 通道里是连续的字节串,没有分割标记区分 "请求 A 完整结束、请求 B 开始"。 服务器内核收到一堆连续字节,必须逐字符扫描\r\n\r\n分割出完整请求报文,没读到完整的一段文本请求前,线程根本无法开始处理这个请求

极端情况:请求 A 是一张超大图片,报文分段分批发到服务器,TCP 管道里塞满请求 A 的字节;哪怕请求 B 的小报文早就发送完成,混杂在后面,服务器也得等请求 A 完整分割完毕,才能读取、解析请求 B。 这就是 HTTP1.1 无法根治的队头阻塞,和服务器是不是多线程并行处理毫无关系。

疑问 2:二进制帧是解决「TCP 字节流无边界」的底层载体,是实现单连接并发的必要前提

HTTP/2 的二进制帧自带固定 9 字节帧头,提前标注了:当前帧数据长度、帧归属的流 ID。 带来两个关键能力,是纯文本报文做不到的:

  1. 天然报文边界,不用扫描分隔符 服务器读取 9 字节头部,立刻知道这一帧一共多少字节,直接截取对应长度数据;不需要逐字符查找换行符,哪怕不同请求的帧交错混在一起,也能瞬间拆分。
  2. 通过 Stream ID 隔离不同请求 每一条独立请求分配唯一流 ID,不同请求的数据被切割成独立帧。 传输顺序可以完全打乱:流 1 的一段数据帧、流 2 的完整头部帧、流 3 的数据帧交替传输。 服务器收到任意一帧,根据流 ID 丢给对应线程处理,不需要等待某一个请求的全部数据到达

对比你的假设:只用二进制报文、不用帧结构,依旧不行

如果你只是把整个请求整体转为二进制、不拆分为带长度标记的独立帧: 本质还是一整块连续二进制数据流,依旧没有分段边界。大块请求的二进制数据占满 TCP 通道时,后面小请求的二进制块依然会被阻塞,服务器再怎么多线程并行也无法提前读取后面的请求。

一句话区分两个角色(分清谁负责什么)

  1. 服务器多线程 / 协程 :负责并行执行业务逻辑(并发计算、查库);
  2. 二进制帧结构 :负责在 TCP 传输层实现多请求数据交错传输、互不阻塞; 二者缺一不可。没有二进制帧,单 TCP 管道天然存在队头阻塞,服务器再强的并行能力也无从发挥。

极简总结

  1. TCP 是无边界字节流,HTTP1.1 文本报文靠换行分割,必须读完一整个请求才能处理,会发生队头阻塞;
  2. 二进制帧自带长度字段,天然区分数据边界,搭配 Stream ID 可以把多个请求拆成碎片乱序传输;
  3. 服务器并行只能加快业务处理,不能改变 TCP 流的排队特性;二进制帧才是实现单 TCP 并发多请求的底层基础。

四、服务端主动推送数据

1.先讲核心前提:推送必须基于客户端一次正常请求

HTTP 原生模型永远是「客户端先发请求」,HTTP2 推送不能凭空推送,规则:

  1. 客户端发正常请求(比如请求首页 GET /index.html);
  2. 服务器解析首页后,预判页面一定会依赖配套资源(css、js、图片);
  3. 服务器主动把这些配套资源推送给客户端,不用等客户端再单独发请求。

举个场景: 浏览器请求首页 index.html,页面里引用了 style.cssmain.js。 HTTP1.1 流程: 浏览器拿到 html,解析到 css/js → 再分别发送 2 次请求下载文件。 HTTP2 Push 流程: 服务器收到 html 请求后,不等浏览器解析页面,主动把 css、js 一起发给浏览器,省去后续两次请求。

2.底层怎么实现?依靠 HTTP2 二进制帧(PUSH_PROMISE 帧)

推送的核心载体是 PUSH_PROMISE 二进制帧,拆解工作流程:

  1. 客户端发起普通请求 客户端发送 HEADERS 帧(Stream A,流 ID 奇数,客户端创建的流)请求 /index.html

  2. 服务器发送 PUSH_PROMISE 推送承诺帧 服务器判断需要推送 style.css

    • 新建一个推送流 Stream B(偶数流 ID,服务器主动创建);
    • 发送 PUSH_PROMISE 帧给客户端,帧内包含两件事: ① 要推送的资源路径 /style.css; ② 本次推送对应的父流 ID(也就是请求 html 的 Stream A)。

    PUSH_PROMISE = 推送预告:先告诉客户端 "我马上给你推送这个文件"。

  3. 客户端确认是否接收推送 客户端本地有缓存:如果本地已经存在最新的 style.css,可以发送 RST_STREAM 帧直接拒绝本次推送,服务器就停止传输该资源; 如果没有缓存,客户端静默等待接收推送数据。

  4. 服务器推送资源完整数据 服务器通过刚刚创建的偶数推送流 Stream B,发送 HEADERS 帧(响应头)+ DATA 帧(css 文件二进制内容),把完整资源推送到客户端。

  5. 客户端缓存推送资源 客户端把推送过来的 css 存入本地缓存,后续解析 html 需要加载 css 时,直接读取缓存,不再发送网络请求。

3.关键细节补充

1、流 ID 规则区分普通流、推送流

  • 奇数 StreamID:客户端主动创建(正常页面、接口请求);
  • 偶数 StreamID:服务器主动创建(仅用于 Push 推送资源)。

2、推送的限制(为什么现在很少用 Push)

  1. 推送是服务器预判,很容易过度推送:客户端资源已经缓存,服务器还强行推送,浪费带宽;
  2. 无法精准感知客户端本地缓存状态,只能盲目推送;
  3. HTTP3(QUIC)直接废弃了 Server Push 功能,主流网站(抖音、B 站)基本关闭 HTTP2 推送。

4.极简总结

  1. HTTP2 服务端推送不能主动凭空发资源,必须依托客户端的一次原始请求;
  2. 核心依靠 PUSH_PROMISE 二进制推送预告帧,服务器提前告知客户端要推送的资源路径;
  3. 服务器新建偶数 ID 的推送流,主动传输配套 css/js/ 图片资源;客户端可根据本地缓存拒绝推送;
  4. 作用:提前推送页面依赖资源,省去客户端二次请求,优化加载速度;但因过度推送问题,线上很少开启。
相关推荐
杏花春雨江南2 小时前
JavaWeb HTTP长连接完整梳理
网络·网络协议·http
tiantianuser3 小时前
NVME-oF IP 设计9 :为什么选RDMA传输?
网络·网络协议·tcp/ip
tiantianuser4 小时前
NVME-oF IP 设计8 :设计扫盲6
网络协议·rdma·高速传输·nvme-of
超级赛博搬砖工5 小时前
并发、多线程和HTTP连接之间有什么关系?
网络·网络协议·http
2401_891957317 小时前
简单了解传输层协议UDP
网络·网络协议·udp
一棵星8 小时前
WebSocket 长连接不稳?一次完整的排查与修复之旅
网络·websocket·网络协议
JackieDYH8 小时前
WebSocket-不同平台封装-Hooks和使用案例
网络·websocket·网络协议
牛马工作号1 天前
Wi‑Fi 完全指南:从 802.11 协议到 Wi‑Fi 7、AC+AP 与 Mesh 工程组网
网络·网络协议·智能路由器
Geek-Chow1 天前
ALB SSL policy conflict (AWS Load Balancer Controller)
网络协议·ssl·aws