开始前,会这些就够了
- 认识请求与响应;能运行一个 Python 文件
一家店的门一直开着,不代表里面永远不用排队。连接复用也类似:避免每次都重新建立连接,与多件事能否交错推进,是两项不同能力。
本篇讨论客户端到服务端的一段 HTTP 连接。数据库连接池另有自己的协议与使用规则,不能把这里的多路复用结论直接搬过去。
先把“下次继续用”说明白
连接复用表示多个请求可以使用已经建立的连接,减少反复建立与关闭的动作。HTTP/1.1 支持持久连接,但连接仍可能因空闲超时、错误或对端决定而关闭;客户端要处理它不能继续用的情况。
只要一条连接上出现过多个请求,就能说明它被多次使用,却不能据此证明它们同时进行。先做完甲再做乙,也属于复用。观察时要同时看连接身份和各请求的时间线。
HTTP/1.1 可以先发多个请求,响应仍有顺序要求
协议允许支持流水线的客户端在前一个响应回来前继续发请求。HTTP/1.1 的对应响应必须按请求收到的顺序发送,因此前面的响应迟迟没完成,后面的响应仍可能被挡住。这叫队头阻塞:排头的问题影响后面。
这项协议能力不代表每个浏览器、代理或客户端默认启用流水线。另开连接可以改变某些等待,但也会增加资源占用。本文不提供“固定开六条就最好”一类脱离客户端和负载的数字。
给不同请求的数据块带上各自编号
HTTP/2 把一个请求与响应放到一个 stream,也就是逻辑数据流。帧携带流编号,来自不同流的数据可以在同一连接上交错出现,接收端再按编号归回各自的请求。因此,小响应有机会在大响应还没结束时先完成。
下面只拿已经拆好的几块字节演示归组:编号 1 的内容分两块,编号 3 只要一块。它没有实现 HTTP 帧解析、头部压缩、流控或真实网络;输出只说明“知道每块属于谁”怎样允许交错。
# An illustration using already-parsed pieces, not an HTTP/2 implementation.
pieces = [(1, b"page-", False), (3, b"icon", True), (1, b"body", True)]
buffers = {}
for stream_id, payload, done in pieces:
buffers.setdefault(stream_id, bytearray()).extend(payload)
if done:
print("stream", stream_id, "finished:", bytes(buffers.pop(stream_id)))
assert not buffers
怎样运行:Python 3.10+ 标准库;保存为 stream_pieces.py,运行 python stream_pieces.py。
运行后对照结果
stream 3 finished: b'icon'
stream 1 finished: b'page-body'
分开数据流,也不等于各自拥有一套网络
HTTP/2 的底层仍是 TCP。TCP 需要按顺序交付字节,一段丢失时,后到但排在它后面的字节可能等待重传,影响同一连接中的多个流。HTTP/2 缓解的响应顺序问题,与底层的这一层等待要分开。
HTTP/3 使用 QUIC 流,一个流因丢包等待时,其他流可以继续推进;但共同带宽、连接流控、服务端处理和头部压缩依赖等约束仍存在。不能把它简化成“丢包不影响速度”或“所有等待都消失”。
看实际用了什么,也看时间花在哪里
排查慢请求时,可记录协商得到的协议、连接是否新建、等待发送、等待服务端响应和下载分别用了多久。浏览器到代理与代理到后端还可能使用不同协议,看到页面一侧是 HTTP/2,并不能推断整条链路全部相同。
协议提供传输能力,应用还受并发上限与计算能力影响。要比较性能,需要固定页面内容、网络条件与服务端负载,并观察成功率和时间分布;本文没有进行这个测评,也不承诺升级后必然更快。
记住这三点
- 连接复用回答是否重复建连,多路复用回答多条流如何共用连接。
- HTTP
/1.1流水线、HTTP/2流与 HTTP/3QUIC 流有不同顺序和阻塞边界。 - 协议版本不能单独证明整体性能,实际链路还可能分成不同协议的多段。
别把例子用过头
- Python 字节归组只是概念模型,不能当作 HTTP
/2客户端或抓包证据。 - 多路复用不代表无限流数、无限带宽或无限服务端并发。
- HTTP
/3仍可能等待资源和依赖,不是“彻底没有队头阻塞”的万能说法。
自己试一小步
如果编号 1 的第二块暂时没有到达,编号 3 能否组装完成?要怎样区分这段模型与 TCP 上的真实情况?
- 先只删除示例中一块编号 1 的数据,记录剩余缓存和已完成流。
- 再画一条共同字节序列,标记中间丢失的一段,说明模型遗漏了哪一层。
- 为真实浏览器观察列出协议、连接身份、等待阶段三项证据。
做到这些,就算完成
- 不把流编号当作独立的物理连接。
- 没有从字典归组实验推导出网络吞吐量。
- 能说明用户侧连接与代理后端连接可能不同。
查原始资料
想确认一个细节,先看这份资料对应的部分,不必从头读完所有文档。
- RFC 9112:HTTP/1.1 (新标签页)↗
第 9.2、9.3、9.3.2、9.4、9.5 节:响应对应顺序、持久连接、流水线与并发连接边界。
- RFC 9113:HTTP/2 (新标签页)↗
第 1、2、4.1、5.2 节:交错传输、流编号、流控以及仍存在的 TCP 队头阻塞。
- RFC 9114:HTTP/3 (新标签页)↗
第 2、4.2.1 节:独立 QUIC 流与头部压缩仍可能带来的依赖。
找一份资料继续练
先选一项,按下面的范围继续学;不必从头读完整门课程。
HTTP 入门:读懂请求、响应和状态码 (新标签页)↗
只读 HTTP 流中的连接、请求和响应步骤,再对照本文区分省掉重复建连与交错传送多个请求的数据。
查看学习准备与访问说明 →浏览器网络面板:查清哪个请求慢了或失败了 (新标签页)↗
只练网络面板的 Timing,分开记录连接准备与等待响应的耗时,不把两条请求同时出现就当成多路复用的证明。
查看学习准备与访问说明 →