# 连接管理

## 短连接

短连接，short-lived connections。

早期的 HTTP 协议使用短连接，在收到响应之后就会立即关闭连接，传输效率很低，大约 2/5=40%。

* 三次握手：3 个数据包，需要 1 个 RTT
* 一个 HTTP 请求和响应：4 个数据包，2 个 RTT
* 四次挥手：4 个数据包需要 2 个 RTT

## 长连接

长连接（long-lived connections），也叫持久连接（persistent connections）、连接保活（keep alive）、连接复用（connection reuse）。

思路：成本均摊，就是把 TCP 的连接和关闭的时间成本由原来的一个“请求-应答”均摊到多个“请求-应答”上。这样虽然不能改善 TCP 的连接效率，但基于分母效应，每个“请求-应答”的无效时间就会降低不少，整体传输效率也就提高了。

HTTP/1.1 会默认启用长连接，就是在一个连接上收发多个“请求-响应”。

<table><thead><tr><th width="246.33333333333331">header 字段</th><th width="77">client</th><th width="85">server</th><th>说明</th></tr></thead><tbody><tr><td>Connection: keep-alive</td><td>✔</td><td><mark style="color:purple;">✔</mark></td><td><p>启用长连接</p><p>在请求头里明确地要求使用长连接机制</p></td></tr><tr><td></td><td></td><td></td><td>不过通常是不管客户端是否要求长连接，只要服务器支持，总会在响应报文里放上该字段来告诉客户端“我是支持长连接的，接下来就用这个 TCP 一直收发数据吧”</td></tr><tr><td>Connection: close</td><td><mark style="color:purple;">✔</mark></td><td>✔</td><td>客户端要主动关闭连接，于是服务器会在响应报文里也加上这个字段，且在发送之后就调用 Socket API 关闭 TCP 连接</td></tr><tr><td>Keep-Alive: timeout=value</td><td>✔</td><td>✔</td><td>限定长连接的超时时间<br>不太常见，约束力并不强</td></tr></tbody></table>

缺点：过多的长连接会占用服务器资源。因为 TCP 连接长时间不关闭，服务器必须在内存里保存它的状态，这就占用了服务器的资源。如果有大量的空闲长连接只连不发，就会很快耗尽服务器的资源，导致服务器无法为真正有需要的用户提供服务。

解决思路：长连接需要在恰当的时间关闭，不能永远保持与服务器的连接（客户端和服务器都可以做）

解决办法：

1. 客户端在请求头里加上“Connection: close”字段，告诉服务器“这次通信后就关闭连接”
2. 客户端和服务器都可以在报文里附加通用头字段“Keep-Alive: timeout=value”
3. 服务器端通常不会主动关闭连接，但也可以使用一些策略。比如 Nginx，它有两种方式：
   * `keepalive_timeout` 指令，设置长连接的超时时间
   * `keepalive_requests` 指令，设置长连接上可发送的最大请求次数

```nginx
# 如果在一段时间内，连接上没有任何数据收发，就主动断开连接。从而避免空闲连接占用系统资源
keepalive_timeout ;

# 当 Nginx 在这个连接上处理了 1000 个请求之后，就会主动断开连接
keepalive_requests 1000;
```

## 队头阻塞

队头阻塞，head-of-line blocking，也叫队首阻塞。它会导致性能下降。

队头阻塞，与短连接和长连接无关，而是由 HTTP 基本的“请求-应答”模型导致的。

因为 HTTP 规定报文必须是“一发一收”，这就形成了一个先进先出的串行队列。队列里的请求没有轻重缓急的优先级，只有入队的先后顺序，排在最前面的请求被最优先处理。如果队首的请求因为处理的太慢耽误了时间，那么队列里后面的所有请求也不得不跟着一起等待，结果就是其他的请求承担了不应有的时间成本。

考虑到“请求-应答”的模型不能变，所以队头阻塞的问题在 HTTP/1.1 里无法解决，只能缓解。两个技术：

1. 并发连接，concurrent connections。即同时对一个域名发起多个长连接，用数量来解决质量问题
   * 缺陷：如果每个客户端都想自己快，建立很多个连接，“用户数×并发数”就会是个天文数字。服务器的资源根本就扛不住，或者被服务器认为是恶意攻击，反而会造成“拒绝服务”
   * HTTP 协议建议客户端使用并发，但不能滥用并发。RFC2616 明确限制每个客户端最多并发 2 个连接。不过实践证明这个数字实在是太小了，众多浏览器都无视标准，把这个上限提高到了 6\~8。后来修订的 RFC7230 也就取消了 2 的限制
   * 然而，由并发连接所榨出的性能跟不上高速发展的互联网无止境的需求
2. 域名分片，domain sharding。还是用数量来解决质量的思路
   * HTTP 协议和浏览器会限制并发连接数量，那就多开几个域名，且都指向同一台服务器
   * 比如 a.xxx.com、b.xxx.com 都指向 [www.xxx.com，如此实际的长连接数量就又上去了](http://www.xxx.com，如此实际的长连接数量就又上去了)


---

# Agent Instructions: Querying This Documentation

If you need additional information that is not directly available in this page, you can query the documentation dynamically by asking a question.

Perform an HTTP GET request on the current page URL with the `ask` query parameter:

```
GET https://anjia1.gitbook.io/cs/http/header/connection.md?ask=<question>
```

The question should be specific, self-contained, and written in natural language.
The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
