所以根据haproxy作者,谁知道一两件事关于http:
发明 Keep-alive 是为了在 CPU 慢 100 倍时减少服务器上的 CPU 使用率。但是没有说的是持久连接会消耗大量内存,而除了打开它们的客户端之外,任何人都无法使用。在 2009 年的今天,CPU 非常便宜,内存仍然受限于架构或价格的几 GB。如果一个站点需要保持活动,那就是一个真正的问题。高负载站点通常禁用保持活动以支持最大数量的同时客户端。没有 keep-alive 的真正缺点是获取对象的延迟略有增加。浏览器将非保活站点上的并发连接数加倍以弥补这一点。
这符合其他人的经验吗?即没有keep-alive - 结果现在几乎不明显吗? (可能值得注意的是,使用 websockets 等 - 无论保持活动状态如何,连接都保持“打开” - 对于响应速度非常快的应用程序)。对于远离服务器的人来说效果是否更大 - 或者在加载页面时从同一主机加载许多工件? (我认为 CSS、图像和 JS 之类的东西越来越多地来自缓存友好的 CDN)。
想法?
(不确定这是否是 serverfault.com 的问题,但在有人告诉我把它移到那里之前,我不会交叉发布)。
嘿,因为我是这篇引文的作者,我会回应:-)
大型站点存在两个大问题:并发连接和延迟。并发连接是由下载内容需要很长时间的慢速客户端和空闲连接状态引起的。这些空闲连接状态是由连接重用以获取多个对象引起的,称为保持活动状态,延迟会进一步增加。当客户端非常靠近服务器时,它可以密集使用连接并确保它几乎从不空闲。然而,当序列结束时,没有人关心快速关闭通道,并且连接长时间保持打开和未使用状态。这就是为什么许多人建议使用非常低的保持活动超时的原因。在像 Apache 这样的服务器上,您可以设置的最低超时时间是一秒,而且对于维持高负载来说通常太多了:如果您面前有 20000 个客户端并且它们平均每秒获取一个对象,您将永久建立这 20000 个连接。像 Apache 这样的通用服务器上的 20000 个并发连接是巨大的,需要 32 到 64 GB 的 RAM,具体取决于加载的模块,即使添加 RAM,您也可能不希望更高。实际上,对于 20000 个客户端,您甚至可能会在服务器上看到 40000 到 60000 个并发连接,因为如果浏览器要获取许多对象,它们会尝试建立 2 到 3 个连接。
如果在每个对象之后关闭连接,并发连接数将急剧下降。实际上,它将下降一个因子,该因子对应于对象之间的时间下载对象的平均时间。如果您需要 50 毫秒来下载一个对象(一张微型照片、一个按钮等...),并且如上所述平均每秒下载 1 个对象,那么每个客户端只有 0.05 个连接,也就是只有 1000 个20000 个客户端的并发连接。
现在是时候建立新的连接了。远程客户端将经历令人不快的延迟。过去,浏览器在禁用 keep-alive 时会使用大量并发连接。我记得在 MSIE 上是 4,在 Netscape 上是 8。这实际上会将每个对象的平均延迟除以那么多。现在keep-alive无处不在,我们再也看不到这么高的数字了,因为这样做进一步增加了远程服务器的负载,而浏览器负责保护互联网的基础设施。
这意味着在当今的浏览器中,让非保活服务与保活服务一样具有响应性变得更加困难。此外,一些浏览器(例如:Opera)使用启发式方法来尝试使用流水线。流水线是使用 keep-alive 的一种有效方式,因为它通过发送多个请求而不等待响应几乎消除了延迟。我在一个有 100 张小照片的页面上尝试过,第一次访问的速度大约是没有 keep-alive 的两倍,但下一次访问的速度大约是 8 倍,因为响应非常小,只计算延迟(仅“304”响应)。
我想说理想情况下,我们应该在浏览器中设置一些可调参数,以使它们保持获取的对象之间的连接处于活动状态,并在页面完成时立即将其删除。但不幸的是,我们没有看到这一点。
出于这个原因,一些需要在前端安装通用服务器(例如 Apache)并且必须支持大量客户端的站点通常必须禁用 keep-alive。并且为了强制浏览器增加连接数,它们使用多个域名,以便可以并行下载。这在大量使用 SSL 的网站上尤其成问题,因为连接设置甚至更高,因为还有一个额外的往返行程。
现在比较常见的是,这类站点更喜欢安装轻量级前端,比如 haproxy 或 nginx,处理几万到几十万个并发连接没有问题,它们在客户端启用 keep-alive,在客户端禁用它阿帕奇方面。在这方面,建立连接的成本在 CPU 方面几乎为零,在时间方面根本不明显。这样一来,这提供了两全其美的效果:由于保持活动的低延迟,客户端的超时非常低,服务器端的连接数量少。大家都开心 :-)
一些商业产品通过重用前端负载均衡器和服务器之间的连接并在它们上多路复用所有客户端连接来进一步改进这一点。当服务器靠近 LB 时,增益不会比以前的方案高多少,但通常需要对应用程序进行适配,以确保不会因多个用户之间意外共享连接而导致用户之间会话交叉的风险.理论上这不应该发生。现实有很大不同:-)
自从写这篇文章(并在 stackoverflow 上发布)以来的几年里,我们现在拥有像 nginx 这样的服务器,它们越来越受欢迎。
例如,nginx 可以在一个只有 2.5 MB(兆字节)RAM 的进程中保持打开的 10,000 个保持连接。事实上,使用很少的 RAM 很容易保持打开数千个连接,并且您会遇到的唯一限制是其他限制,例如打开文件句柄或 TCP 连接的数量。
Keep-alive 是一个问题,不是因为 keep-alive 规范本身有任何问题,而是因为 Apache 的基于进程的扩展模型和 keep-alives 侵入了架构不是为适应它而设计的服务器。
特别有问题的是 Apache Prefork + mod_php + keep-alives。这是一个模型,其中每个连接都将继续占用 PHP 进程占用的所有 RAM,即使它完全空闲并且仅作为保持活动状态保持打开状态。这是不可扩展的。但是服务器不必以这种方式设计 - 没有特别的理由服务器需要将每个保持活动连接保持在单独的进程中(尤其是当每个这样的进程都有完整的 PHP 解释器时)。 PHP-FPM 和 nginx 等基于事件的服务器处理模型优雅地解决了这个问题。
2015 年更新:
SPDY 和 HTTP/2 用更好的东西取代了 HTTP 的 keep-alive 功能:不仅可以保持连接并在其上发出多个请求和响应,而且可以多路复用,因此可以以任何顺序发送响应,并且是并行的,而不仅仅是按照请求的顺序。这可以防止缓慢的响应阻塞更快的响应,并消除浏览器保持打开多个并行连接到单个服务器的诱惑。这些技术进一步突出了 mod_php 方法的不足之处,以及基于事件(或至少是多线程)的 Web 服务器与 PHP-FPM 之类的东西分开耦合的好处。
我的理解是,它与 CPU 无关,而是打开重复套接字到世界另一端的延迟。即使您有无限带宽,连接延迟也会减慢整个过程。如果您的页面有几十个对象,则放大。即使是持久连接也有请求/响应延迟,但是当您有 2 个套接字时它会减少,因为平均而言,一个应该是流数据,而另一个可能是阻塞的。此外,在让您写入之前,路由器永远不会假设套接字已连接。它需要完整的往返握手。再说一次,我不声称自己是专家,但这就是我一直以来的看法。真正酷的是一个完全异步的协议(不,不是一个完全病态的协议)。
如果您使用 CloudFront 或 CloudFlare 等“来源拉取”CDN,那么非常长的保活会很有用。事实上,这比没有 CDN 更快,即使您提供的是完全动态的内容。
如果您长期保持活动状态,以至于每个 PoP 基本上都与您的服务器建立了永久连接,那么当用户第一次访问您的站点时,他们可以与本地 PoP 进行快速 TCP 握手,而不是与您进行慢速握手。 (光本身通过光纤绕半个地球大约需要 100 毫秒,而建立 TCP 连接需要来回传递三个数据包。SSL requires three round-trips。)
不定期副业成功案例分享