http学习 http的连接管理

短连接

HTTP 协议最初(0.9/1.0)是个非常简单的协议,通信过程也采用了简单的“请求 - 应答”方式。

它底层的数据传输基于 TCP/IP,每次发送请求前需要先与服务器建立连接,收到响应报文后会立即关闭连接。因为客户端与服务器的整个连接过程很短暂,不会与服务器保持长时间的连接状态,所以就被称为“短连接”(short-lived connections)。

早期的 HTTP 协议也被称为是“无连接”的协议。短连接的缺点相当严重,因为在 TCP 协议里,建立连接和关闭连接都是非常“昂贵”的操作。

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

很显然,短连接的缺点严重制约了服务器的服务能力,导致它无法处理更多的请求。

 

ps:一个来回就是1rtt,三次回收准确来说是1.5个rtt,四次挥手是两个来回,所以是2rtt。

 

 

长连接

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

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

这样虽然不能改善 TCP 的连接效率,但基于“分母效应”,每个“请求 - 应答”的无效时间就会降低不少,整体传输效率也就提高了。

下图是短连接与长连接的对比示意图。

 

 

 

连接相关的头字段

由于长连接对性能的改善效果非常显著,所以在 HTTP/1.1 中的连接都会默认启用长连接。

不需要用什么特殊的头字段指定,只要向服务器发送了第一次请求,后续的请求都会重复利用第一次打开的 TCP 连接,也就是长连接,在这个连接上收发数据。

当然,我们也可以在请求头里明确地要求使用长连接机制,使用的字段是 Connection,值是“keep-alive”。

不过不管客户端是否显式要求长连接,如果服务器支持长连接,它总会在响应报文里放一个“Connection: keep-alive”字段,告诉客户端:“我是支持长连接的,接下来就用这个 TCP 一直收发数据吧”。

 

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

所以,长连接也需要在恰当的时间关闭,不能永远保持与服务器的连接,这在客户端或者服务器都可以做到。

在客户端,可以在请求头里加上“Connection: close”字段,告诉服务器:“这次通信后就关闭连接”。服务器看到这个字段,就知道客户端要主动关闭连接,于是在响应报文里也加上这个字段,发送之后就调用 Socket API 关闭 TCP 连接。

服务器端通常不会主动关闭连接,但也可以使用一些策略。拿 Nginx 来举例,它有两种方式:

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

另外,客户端和服务器都可以在报文里附加通用头字段“Keep-Alive: timeout=value”,限定长连接的超时时间。但这个字段的约束力并不强,通信的双方可能并不会遵守,所以不太常见。

 

 

队头阻塞

 

“队头阻塞”

Head-of-line blocking,也叫“队首阻塞”

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

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

如果队首的请求因为处理的太慢耽误了时间,那么队列里后面的所有请求也不得不跟着一起等待,结果就是其他的请求承担了不应有的时间成本。

 

性能优化

  • 方案1:并发连接”(concurrent connections)

“同时对一个域名发起多个长连接,用数量来解决质量的问题。”HTTP 允许客户端使用并发,但不能滥用。

  • 方案2:“域名分片”。

HTTP 协议和浏览器不是限制并发连接数量吗?好,那我就多开几个域名,比如 shard1.chrono.com、shard2.chrono.com,而这些域名都指向同一台服务器 www.chrono.com。

注意:域名分片解决的是客户端并发的问题,可以创建更多的连接。并不是解决服务端问题。

 

 

小结

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

 

 

问答

1、在开发基于 HTTP 协议的客户端时应该如何选择使用的连接模式呢?短连接还是长连接?

答:这个视业务场景而定,只要一次交互就行了,也就是只有一次来回就OK,那就短链接,否则就长连接。

 

2、应当如何降低长连接对服务器的负面影响呢?

答:使用一定的保护措施,比如:按超时时间或请求次数来关闭连接,也可以搞一个连接池来防护服务器。

 

 

补充:

1、队头阻塞在http和tcp层次都有,原因不同。

对于tcp队头阻塞:每个 TCP 分组都会带着一个唯一的序列号被发出,而所有分组必须按顺序传送到接收端。如果中途有一个分组没能到达接收端,那么后续分组必须保存到接收端的 TCP 缓冲区,等待丢失的分组重发并到达接收端。这一切都发生在 TCP 层,应用程序对 TCP 重发和缓冲区中排队的分组一无所知,必须等待分组全部到达才能访问数据。在此之前,应用程序只能在通过套接字读数据时感觉到延迟交互。这种效应称为 TCP 的队首阻塞。

 

2、RTT(Round-Trip Time): 往返时延。

在计算机网络中它是一个重要的性能指标,表示从发送端发送数据开始,到发送端收到来自接收端的确认(接收端收到数据后便立即发送确认),总共经历的时延。

一般认为单向时延=传输时延t1+传播时延t2+排队时延t3

t1是数据从进入节点到传输媒体所需要的时间,通常等于数据块长度/信道带宽

t2是信号在信道中需要传播一定距离而花费的时间,等于信道长度/传播速率(光纤中电磁波的传播速率约为2*10^5 km/s,铜缆中2.3*10^5 km/s)

t3可笼统归纳为随机噪声,由途径的每一跳设备及收发两端负荷情况及吞吐排队情况决定(包含互联网设备和传输设备时延)

posted @ 2022-02-10 17:48  r1-12king  阅读(124)  评论(0编辑  收藏  举报