15. 长连接与短连接 / keep-alive(热度 221 · 进阶)
Q: HTTP 长连接和短连接是什么?Connection: keep-alive 有什么行为?
A:
概念与行为
- 短连接: 每个请求建一次 TCP(三次握手),响应后关闭(四次挥手)。HTTP/1.0 默认。
- 长连接: 同一 TCP 上串行复用多个 HTTP 请求/响应。HTTP/1.1 默认 keep-alive(除非显式
Connection: close);HTTP/1.0 需双方显式开启。 - HTTP/2/3: 连接更持久,且多路复用/独立流,keep-alive 并入连接生命周期管理。
行为细节
- 空闲超时与最大请求数:
Keep-Alive: timeout=N, max=M;Nginx 对应keepalive_timeout、keepalive_requests。 - 谁关闭: 达到 timeout/max、错误或显式 close 时,通常由服务器发起关闭。
- 竞态: 服务端关闭时客户端可能正在发下一条请求 → 可能失败;HTTP 允许对幂等方法重试。
- TIME_WAIT 分布: 谁主动关闭谁 TIME_WAIT;长连接可显著减少服务端 TIME_WAIT。
设计动机
- 减少建连开销: 每个短连接 1 RTT(TCP)+ 可能的 TLS 握手;页面数十资源时代价巨大。
- 减少 TIME_WAIT/端口压力: 高 QPS 服务端尤其实惠。
- 为何仍不能解决队头阻塞: HTTP/1.1 长连接上请求仍需串行/有限并发,应用层队头阻塞仍在;HTTP/2 多路复用才是根治(传输层见 HTTP/3)。
常见追问
追问 1:keep-alive 省了什么?没省什么?
- 答题要点: 省了重复 TCP(及 TLS)握手 RTT 与状态维护成本;没省单次请求的服务端处理时间,也没解决 HTTP/1.1 应用层队头阻塞。
追问 2:长连接会不会耗尽服务器资源?
- 答题要点: 会。空闲连接占 fd/内存;需 timeout、max requests、连接数限制、LB 超时对齐。大量 CLOSE_WAIT 则是应用没关 socket 的 bug(见第 18 题)。
追问 3:客户端如何配合长连接?
- 答题要点: 连接池复用、不要每请求新建;尊重
Connection: close;对连接被服务端关断的请求做幂等重试;监控连接错误率。
易错点
| 易错说法 | 纠正 |
|---|---|
| 「keep-alive 是 HTTP/2 新特性」 | HTTP/1.1 已默认;1.0 需显式开启 |
| 「长连接=WebSocket」 | 不同:长连接仍是 HTTP 请求-响应复用;WS 是全双工帧协议 |
| 「长连接一定更快」 | 有首字节慢/阻塞时仍可能排队;资源未复用时收益有限 |
| 「TIME_WAIT 多就关 keep-alive」 | 通常应加强连接复用,而不是更碎地开关连接 |
| 「Connection 头对 HTTP/2 语义完全相同」 | HTTP/2 连接管理不同,hop-by-hop 头处理有专门规则 |
与选择题对照
- 主库 150: 第 145 题(
Connection: keep-alive含义)、第 84 题(HTTP 特点含持久连接)、第 92 题(HTTP 演进)、第 60/62/63/138 题(挥手与状态,长连接影响 TIME_WAIT/CLOSE_WAIT)。 - 补题 16: 无直接 keep-alive 题。
- 口述联动: 选择题直接考头字段含义;面试要连接池、超时竞态、与多路复用的关系。
工程 / 抓包背景
- Nginx:nginx
keepalive_timeout 65s; keepalive_requests 1000; # 上游连接池 upstream backend { server 127.0.0.1:8080; keepalive 32; } location / { proxy_pass http://backend; proxy_http_version 1.1; proxy_set_header Connection ""; } - curl: 多次请求看是否复用连接(
-w计时;verbose 可见Re-using existing connection)。 - ss: 对比
ss -tan中 ESTABLISHED 与 TIME_WAIT 数量变化。 - Wireshark: 观察多条 HTTP 请求是否在同一 TCP stream(
tcp.stream eq N)。 - 客户端: OkHttp/Go http.Client/Java HttpClient 连接池默认行为不同,需统一超时。