跳转至

HTTP Keep-Alive

建一次连接,连续发多个请求,不用每次重新握手——HTTP/1.1 最重要的默认优化


核心概念

  1. Keep-Alive — TCP 连接建立后不立即关闭,允许在同一连接上发送多个 HTTP 请求
  2. Connection 头 — 通过 Connection: Keep-Alive 或 Connection: close 控制连接生命周期
  3. 队头阻塞 — Keep-Alive 连接上的请求严格串行,后一个请求必须等前一个响应返回
  4. 演进路线 — 从无 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 参数:

HTTP/1.1 200 OK
Connection: Keep-Alive
Keep-Alive: timeout=5, max=100
字段 含义
Connection: Keep-Alive 告诉客户端:这个连接可以复用
timeout=5 空闲超过 5 秒没新请求,关闭连接
max=100 单连接最多处理 100 个请求,之后强制关闭

客户端也可以主动声明:

GET /page.html HTTP/1.1
Host: example.com
Connection: Keep-Keep-Alive

不想复用时:

HTTP/1.1 200 OK
Connection: close

队头阻塞问题

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 个请求后关闭。