HTTP Keep-Alive¶
建一次连接,连续发多个请求,不用每次重新握手——HTTP/1.1 最重要的默认优化
核心概念¶
- Keep-Alive — TCP 连接建立后不立即关闭,允许在同一连接上发送多个 HTTP 请求
- Connection 头 — 通过
Connection: Keep-Alive或Connection: close控制连接生命周期 - 队头阻塞 — Keep-Alive 连接上的请求严格串行,后一个请求必须等前一个响应返回
- 演进路线 — 从无 Keep-Alive → Keep-Alive → HTTP/2 多路复用 → HTTP/3 QUIC
详解¶
没有 Keep-Alive(HTTP/1.0)¶
一个请求一个连接,用完即弃:
sequenceDiagram
participant C as 客户端
participant S as 服务器
loop 每个请求
C->>S: TCP 三次握手
C->>S: TLS 握手(HTTPS)
C->>S: GET /resource
S->>C: 200 OK
C->>S: TCP 四次挥手断开
end
一个网页通常有几十个资源(HTML、CSS、JS、图片……),每个都走一遍握手流程。光握手就比传数据花的时间还多。
有 Keep-Alive(HTTP/1.1 默认)¶
建一次连接,连续发多个请求:
sequenceDiagram
participant C as 客户端
participant S as 服务器
Note over C,S: TCP 连接(就这一次)
C->>S: GET /index.html
S->>C: 200 OK + HTML
C->>S: GET /style.css(同一个连接)
S->>C: 200 OK + CSS
C->>S: GET /app.js(还是同一个)
S->>C: 200 OK + JS
C->>S: GET /logo.png
S->>C: 200 OK + PNG
Note over C,S: 全部搞完,最后才断开
协议层面的协商¶
服务器通过响应头声明 Keep-Alive 参数:
| 字段 | 含义 |
|---|---|
Connection: Keep-Alive |
告诉客户端:这个连接可以复用 |
timeout=5 |
空闲超过 5 秒没新请求,关闭连接 |
max=100 |
单连接最多处理 100 个请求,之后强制关闭 |
客户端也可以主动声明:
不想复用时:
队头阻塞问题¶
Keep-Alive 解决了频繁握手,但引入了队头阻塞:
sequenceDiagram
participant C as 客户端
participant S as 服务器
C->>S: 请求1
Note over C,S: 必须等响应1完全返回
S->>C: 响应1
C->>S: 请求2(才能开始)
S->>C: 响应2
C->>S: 请求3
S->>C: 响应3
请求 2 必须等请求 1 的响应**完全返回**后才能发出。浏览器用**多开 6 个连接**来弥补——一条收银通道排队太慢,所以超市开了 6 个收银台,但每个收银台内部还是串行的。
演进路线¶
graph TD
A[HTTP/1.0 无 Keep-Alive<br/>一请求一连接] -->|加 Connection 头| B[HTTP/1.1 默认 Keep-Alive<br/>一个连接串行多个请求]
B -->|多开 6 个连接弥补| C[浏览器并行连接<br/>缓解队头阻塞]
C -->|彻底重构| D[HTTP/2 多路复用<br/>一个连接上真正并行]
D -->|连 TCP 队头阻塞都干掉| E[HTTP/3 QUIC<br/>基于 UDP 的多路复用]
常见陷阱¶
空闲连接占用资源
Keep-Alive 连接在空闲时仍然占内存和 fd。如果客户端长时间不发新请求,服务器应该主动关闭。合理设置 timeout 参数。
服务器已关连接但客户端不知道
服务器因超时关闭了连接,但客户端下次复用时才发现连接已死 → 报错。需要开启连接健康检查或设置合理的超时。
练习题¶
HTTP/1.1 默认 Keep-Alive,为什么浏览器还要开 6 个连接?
答案
因为 HTTP/1.1 的 Keep-Alive 存在队头阻塞——同一连接上的请求严格串行。开 6 个连接可以并行处理 6 个请求,大幅提升页面加载速度。HTTP/2 的多路复用解决了这个问题,一个连接就够了。
Keep-Alive 的 timeout 和 max 有什么区别?
答案
timeout 控制的是**时间**——空闲多久后关闭连接。max 控制的是**请求数**——单连接处理多少个请求后关闭。两者取先触发的那个。例如 timeout=5, max=100 表示:空闲 5 秒或处理 100 个请求后关闭。