logologo

17 | 排队也要讲效率:HTTP的连接管理

Oct 28 · 10min

归纳

  1. 早期的 HTTP 协议使用短连接,收到响应后就立即关闭连接,效率很低;
  2. HTTP/1.1 默认启用长连接,在一个连接上收发多个请求响应,提高了传输效率;
  3. 服务器会发送“Connection: keep-alive”字段表示启用了长连接;
  4. 报文头里如果有“Connection: close”就意味着长连接即将关闭;
  5. 过多的长连接会占用服务器资源,所以服务器会用一些策略有选择地关闭长连接;
  6. “队头阻塞”问题会导致性能下降,可以用“并发连接”和“域名分片”技术缓解。

RTT

定义往返时间(round trip time,简称RTT)

短连接

短连接的缺点相当严重,因为在 TCP 协议里,建立连接和关闭连接都是非常“昂贵”的操作。TCP 建立连接要有“三次握手”,发送 3 个数据包,需要 1 个 RTT;关闭连接是“四次挥手”,4 个数据包需要 2 个 RTT。

而 HTTP 的一次简单“请求 - 响应”通常只需要 4 个包,如果不算服务器内部的处理时间,最多是 2 个 RTT。这么算下来,浪费的时间就是“3÷5=60%”,有三分之二的时间被浪费掉了,传输效率低得惊人。

长连接

针对短连接暴露出的缺点,HTTP 协议就提出了“长连接”的通信方式,也叫“持久连接”(persistent connections)、“连接保活”(keep alive)、“连接复用”(connection reuse)。

其实解决办法也很简单,用的就是“成本均摊”的思路,既然 TCP 的连接和关闭非常耗时间,那么就把这个时间成本由原来的一个“请求 - 应答”均摊到多个“请求 - 应答”上。

对比

相关头字段

使用的字段是 Connection,值是“keep-alive”。

长连接的缺点

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

  1. 主动关闭

    • 可以在请求头里加上“Connection: close”字段
  2. 配置策略

    • 使用“keepalive_timeout”指令,设置长连接的超时时间,如果在一段时间内连接上没有任何数据收发就主动断开连接,避免空闲连接占用系统资源。
    • 使用“keepalive_requests”指令,设置长连接上可发送的最大请求次数。比如设置成 1000,那么当 Nginx 在这个连接上处理了 1000 个请求后,也会主动断开连接。

对头阻塞

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

因为 HTTP 规定报文必须是“一发一收”,这就形成了一个先进先出的“串行”队列。队列里的请求没有轻重缓急的优先级,只有入队的先后顺序,排在最前面的请求被最优先处理。

优化

  1. 并发链接
    • 这在 HTTP 里就是“并发连接”(concurrent connections),也就是同时对一个域名发起多个长连接,用数量来解决质量的问题。
    • 浏览器规定最多6~8个TCP链接

http和tcp层面的阻塞

队头阻塞有两个层面,一个是HTTP的长连接排队请求处理,另一个是TCP的丢包重传机制。

CC BY-NC-SA 4.0 2021 © Promise L