HTTP 版本、连接复用与超时
HTTP 请求的耗时不仅来自服务端处理,还包括连接建立、数据传输、连接池等待和失败重试。理解 HTTP 各版本如何复用连接,以及每一类超时约束什么阶段,才能定位一次远程调用为什么变慢。
1. HTTP/1.1 通过持久连接减少握手
HTTP/1.1 默认可以在一条 TCP 连接上连续发送多个请求,避免每次请求都重新完成 TCP 和 TLS 握手。连接复用能降低短请求的固定成本,也能减少客户端与服务端同时维护的连接数量。
同一条 HTTP/1.1 连接上的响应仍需按照请求顺序返回。流水线中的前一个响应迟迟没有完成,后面的响应也无法越过它;客户端因此通常建立多条连接并发请求,而不是依赖 HTTP pipelining。
连接复用还会带来新的等待位置:当池中连接已经占满,请求会先等待可用连接。此时服务端可能没有收到请求,排查时不能只看服务端执行时间。
2. HTTP/2 在一条连接中并发传输多个流
HTTP/2 把消息拆成二进制帧,每个请求和响应属于一个 stream。不同 stream 的帧可以交错传输,因此一个较大的响应不会要求其他响应在应用层排队到它之后。HPACK 还会压缩重复的请求头。
HTTP/2 通常用较少的 TCP 连接承载更多并发请求,但它没有消除 TCP 层的队头阻塞。一个 TCP 报文丢失时,接收方必须等待缺失字节重传,位于同一连接上的所有 HTTP/2 stream 都可能暂时停住。
所以,HTTP/2 的多路复用解决的是 HTTP/1.1 的响应顺序限制,并不代表网络丢包对其他请求完全没有影响。
3. HTTP/3 用 QUIC 隔离不同流的丢包
HTTP/3 运行在 QUIC 之上。QUIC 基于 UDP 实现可靠传输,每个 stream 独立维护有序字节流。一个 stream 丢包时,其他已经收到完整数据的 stream 可以继续交付,避免 TCP 连接级别的队头阻塞。
QUIC 还把传输层与 TLS 握手结合起来,并支持连接迁移。移动网络切换地址时,只要连接标识仍然有效,就不必单纯因为 IP 或端口变化重新建立完整连接。
HTTP/3 仍然需要拥塞控制、重传和流量控制。它改善了高延迟或丢包网络下的并发体验,不会让丢失的数据免于重传。
4. 连接池要限制数量与生命周期
客户端连接池至少需要约束以下参数:
- 每个目标地址和整个客户端的最大连接数。
- 等待池中连接的最长时间。
- 空闲连接保留时间与最大生命周期。
- 单条 HTTP/2 或 HTTP/3 连接允许的并发 stream 数。
连接太少会让请求在客户端排队;连接太多则会增加文件描述符、内存、TLS 状态和服务端并发压力。连接长期不更新,还可能继续指向已经摘流的实例,或者被中间网络设备静默关闭。
连接池配置应结合并发量、请求耗时、协议版本和服务端容量压测。不要仅因为出现超时就提高连接数。
5. 超时分别约束调用链的不同阶段
常见超时并不是同一个参数:
- 连接池等待超时:等待可用连接的时间。
- 连接超时:建立 TCP 或 QUIC 连接的时间。
- TLS 握手超时:完成证书校验和密钥协商的时间。
- 写超时:发送请求数据时允许持续阻塞的时间。
- 首字节或响应超时:等待服务端开始或完成响应的时间。
- 空闲超时:连接在没有数据传输时允许保持多久。
- 调用截止时间:从调用方开始执行到必须结束的总预算。
只有响应超时而没有总截止时间时,请求可能先在连接池排队,再经历连接和重试,最终远超调用方允许的时间。更稳妥的方式是由最外层调用方给出 deadline,并向下游传播剩余预算。
6. 重试必须服从剩余时间与操作语义
HTTP 规范把 GET、PUT、DELETE 等方法定义为幂等方法,但业务实现仍需满足对应语义。POST 也可以通过业务幂等键获得可重试能力。客户端在没有收到响应时,无法仅凭网络异常判断服务端是否已经完成写入。
重试时需要同时满足:
- 操作可以安全重复,或者服务端能根据幂等键去重。
- 剩余 deadline 足以完成下一次尝试。
- 使用退避和随机抖动,避免故障时同步放大流量。
- 限制总尝试次数和重试流量比例。
- 明确由哪一层重试,避免客户端、网关和服务同时重复尝试。
7. 用分段指标定位请求耗时
一次 HTTP 调用至少应记录连接池等待、DNS、建连、TLS、请求发送、首字节、响应读取和服务端处理时间。HTTP/2 还要观察活动 stream 数、流量控制窗口和连接级错误。
如果客户端总耗时很高,而服务端处理很快,应优先检查连接池等待、网络重传、代理排队和重试。只有端到端耗时而没有阶段指标,很容易把客户端排队误判成服务端慢查询。
8. 常见问题
8.1 HTTP/2 是否只需要一条连接
不一定。单连接通常可以承载大量并发流,但它仍受 stream 上限、流量控制、拥塞窗口和 TCP 丢包影响。浏览器或客户端也可能因为不同目标地址、代理策略或连接故障维护多条连接。
8.2 read timeout 是否等于接口总超时
通常不等于。不同客户端对 read timeout 的定义可能是等待一次读取、等待首字节或等待整个响应;它一般不包含连接池排队和之前的重试。需要另外设置并传播端到端 deadline。
9. 面试题
9.1 HTTP/1.1、HTTP/2 和 HTTP/3 如何处理并发请求
出现公司:腾讯、字节跳动
考察重点
- 持久连接、多路复用和 stream 的关系。
- HTTP/2 的 TCP 队头阻塞与 HTTP/3 的改进。
- 协议能力与连接池、超时、重试之间的边界。
相关内容:第 1 节“HTTP/1.1 通过持久连接减少握手”至第 6 节“重试必须服从剩余时间与操作语义”。
参考回答
HTTP/1.1 可以复用 TCP 连接,但同一连接上的响应需要保持顺序,客户端通常依靠多条连接获得并发。HTTP/2 把请求拆成带 stream 标识的帧,在一条连接上多路复用,解决应用层的响应排队;底层仍是有序 TCP 字节流,丢包可能暂时阻塞同一连接上的所有流。
HTTP/3 使用 QUIC,让不同 stream 独立交付,一个流丢包不会阻止其他完整流继续处理。无论使用哪个版本,还需要设置连接池上限和端到端 deadline;协议升级不能替代容量、超时与重试治理。