> For the complete documentation index, see [llms.txt](https://anjia1.gitbook.io/cs/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://anjia1.gitbook.io/cs/http/header/connection.md).

# 连接管理

## 短连接

短连接，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，如此实际的长连接数量就又上去了)
