HTTP keep-alive:一条 TCP 连接发多个请求
更新时间:2026-09-17。网络协议域·应用层。它是 HTTP/1.1 相对 1.0 的关键改进;连接池本质是管理一批 keep-alive 连接。高并发服务侧的连接成本见 accept/连接表。
HTTP 早期每个请求都新建 TCP 连接、响应完即关闭。在一个页面要拉几十个资源、API 被高频调用的场景,反复握手开销巨大。keep-alive(持久连接,persistent connection)让一条 TCP 连接在多个请求-响应之间保持打开、顺序复用。
一、短连接 vs 持久连接
短连接(HTTP/1.0 默认):
TCP握手 -> 请求/响应 -> FIN
TCP握手 -> 请求/响应 -> FIN (每次都付握手成本)
keep-alive(HTTP/1.1 默认):
TCP握手(+TLS) -> req1/res1 -> req2/res2 -> req3/res3 ... -> 空闲超时关闭复用一条连接省下:
二、协商与定界
- HTTP/1.1 默认就是持久连接;要关闭显式发
Connection: close。 - HTTP/1.0 默认关闭,需
Connection: keep-alive请求复用。 - 因为连接不关闭,响应必须自带 body 边界——
Content-Length或Transfer-Encoding: chunked(流式),接收方据此知道一个响应在哪结束、下一个请求的响应从哪开始。
HTTP/1.1 200 OK
Keep-Alive: timeout=5, max=100
Connection: keep-alive
Content-Length: 35timeout 是空闲保持秒数、max 是该连接还能服务约多少请求(非强制)。代理(hop-by-hop)会改写/移除 Connection 与 Keep-Alive 这类逐跳头。
三、客户端连接池:keep-alive 的工程形态
浏览器、OkHttp、Go http.Transport、Nginx upstream、数据库驱动等都维护连接池:
- 用后不关闭,放入空闲池;下次请求按目标主机取一条空闲 keep-alive 连接复用。
- 限制每主机/总连接数(浏览器约 6/域名的历史限制)、空闲存活时间、最多复用请求数。
- 新建、健康检查、过期淘汰由池统一管理。
应用请求 -> 池中有空闲连接? --有--> 直接复用(0 握手)
|没有
--> 新建 TCP(+TLS) -> 用完归还池中没有连接池的"短连接 + 高 QPS"会让大量时间花在握手与 TIME_WAIT 上(主动关闭方进入 TIME_WAIT)。微服务间调用务必让 client/server 都开 keep-alive 并配连接池。
四、经典坑:两端超时不一致导致连接重置
最常见的问题是服务端先于客户端关闭了空闲连接:
1. 连接空闲,服务端 keepalive_timeout(如 60s)到,先 FIN/RST
2. 客户端池不知道,仍把这条"半开"连接标记为可用
3. 客户端在上面发请求 -> 收到 connection reset / ECONNRESET(偶发 502)应对:
- 让客户端空闲超时 < 服务端(如客户端 55s、服务端 60s),保证客户端先淘汰。
- 客户端对"在复用连接上的首个请求"做一次安全重试(幂等请求,见方法幂等)。
- 服务端优雅关闭时先停止接收新请求、排空在途请求(connection draining),再 FIN。
- 网关到上游(Nginx upstream)配
keepalive与合理keepalive_timeout,否则常见间歇 502。
五、keep-alive 仍然串行
单条 HTTP/1.1 keep-alive 连接上请求是一来一回严格有序的(pipelining 未普及),并发仍需多连接,存在应用层队头阻塞。HTTP/2 在单连接上多路复用、HTTP/3 在 QUIC 多 stream 复用,把"连接复用"升级为"帧级并发",keep-alive 的概念被内化为长期连接 + 多 stream。
六、观测
ss -tan state established # 持久连接长期处于 ESTABLISHED
ss -tan state time-wait | wc -l # 短连接多时 TIME_WAIT 高(谁主动关谁进)
curl -v https://x.com 2>&1 | grep -i 'reuse\|connection' # curl 是否复用
curl --http1.1 -w '%{time_connect} %{time_appconnect}\n' -o /dev/null -s URL # 握手耗时
# 连接复用后第二次请求 time_connect/time_appconnect 近乎 0相关阅读
总结
HTTP keep-alive 在 HTTP/1.1 默认开启,用 Content-Length/chunked 定界让一条 TCP(+TLS) 连接顺序承载多个请求,省去重复握手并配合连接池复用;要避免服务端先于客户端关闭半开连接引发的偶发 reset(客户端超时更短 + 幂等重试 + 优雅 drain);它仍是单连接串行,最终被 HTTP/2/3 的单连接多路复用升级。