您是否曾在实验室中对服务器进行基准测试,然后将其部署用于实际流量,却发现它无法实现接近基准测试的性能? CPU 利用率很低,而且有大量可用资源,但客户抱怨响应时间太慢,而您又不知道如何提高服务器的利用率。
您所观察到的就是我们所说的“HTTP 繁重工作”的影响之一。 在这篇博文中,我们研究 HTTP 如何运行以及常见的 HTTP 服务器如何处理 HTTP 事务。 我们研究了可能出现的一些性能问题,并了解了 NGINX 的事件驱动模型如何使其成为这些 HTTP 服务器的非常有效的加速代理。 借助 NGINX,您可以转变您的实际性能,使其恢复到本地基准的水平。
有关调整 Linux 和 NGINX 以提高应用速度和可扩展性的指南,请参阅我们博客上的“调整 NGINX 以提高性能” 。
HTTP 保持连接是一项必要的性能功能,可减少延迟并允许网页更快地加载。
HTTP 是一种简单的基于文本的协议。 如果您以前没有这样做过,请查看 HTTP 调试工具(例如 Web 浏览器中的工具)的输出,并检查标准请求和响应结构:
在最简单的实现中,HTTP 客户端会创建到目标服务器的新 TCP 连接,写入请求并接收响应。 然后服务器关闭 TCP 连接以释放资源。
这种操作模式可能非常低效,特别是对于包含大量元素的复杂网页或网络链接较慢的情况。 创建新的 TCP 连接需要“三次握手”,而拆除连接也涉及双向关闭过程。 重复创建和关闭 TCP 连接(每条消息一个),类似于在电话交谈中每个人讲话后挂断电话并重拨。
HTTP 使用一种称为保持活动连接的机制,在 HTTP 事务完成后保持客户端和服务器之间的 TCP 连接保持打开。 如果客户端需要进行另一个 HTTP 事务,它可以使用空闲的保持活动连接,而不是创建新的 TCP 连接。
客户端通常会同时打开多个与服务器的 TCP 连接,并在所有连接之间进行保持活动事务。 这些连接保持打开状态,直到客户端或服务器决定不再需要它们,通常是由于空闲超时。
现代网络浏览器通常会打开 6 到 8 个保持连接,并保持它们打开几分钟,直到超时。 可以配置 Web 服务器以使这些连接超时并尽早关闭它们。
如果大量客户端使用 HTTP 保持活动,并且 Web 服务器存在并发限制或可扩展性问题,那么一旦达到该限制,性能就会下降。
上述方法旨在为单个客户提供最佳的性能。 不幸的是,在类似“公地悲剧”的场景中,如果所有客户端都以这种方式操作,则会对许多常见的 Web 服务器和 Web应用的性能产生不利影响。
原因是许多服务器都有固定的并发限制。 例如,在常见配置中,Apache HTTP Server 只能处理有限数量的并发 TCP 连接: 使用工作多处理模块 (MPM) 时为 150,使用prefork MPM 时为 256。 每个空闲的 HTTP 保持连接都会占用其中一个并发槽,一旦所有槽都被占用,服务器就无法再接受任何 HTTP 连接。
传统观点认为,应该关闭 Web 服务器上的保持活动功能,或者将其限制在很短的生命周期内。 它们为SlowHTTPTest和Slowloris拒绝服务攻击提供了一个非常简单的载体(要获得快速解决方案,请参阅 serverfault.com 上的“防止 Keep-Dead 拒绝服务” )。
此外,这些 Web 和应用服务器通常为每个连接分配一个操作系统线程或进程。 TCP 连接是一个非常轻量的操作系统对象,但是线程或进程是非常重量级的。 线程和进程需要内存,必须由操作系统主动管理,并且线程或进程之间的“上下文切换”会消耗 CPU。 为每个连接分配自己的线程或进程是非常低效的。
大量并发客户端连接以及为每个连接分配一个线程或进程会产生称为“HTTP 重担”的现象,即处理轻量级 HTTP 事务需要付出不成比例的巨大努力。
在许多当代的网络和应用服务器中,不需要很多客户端就能耗尽并发限制。
如果客户端打开 8 个 TCP 连接并在最后一次使用后保持每个连接处于活动状态 15 秒,则客户端将消耗 8 个并发槽,持续 15 秒。 如果客户端以每秒 1 个的速度访问您的网站,则 120 个并发槽将持续被空闲的保持活动连接占用。 如果速率为每秒 2 个客户端,则占用 240 个并发槽。 一旦插槽耗尽,新客户端就无法连接,直到现有连接超时。
这可能会导致服务水平极不均衡。 成功获取保持连接的客户机可以随意浏览您的服务。 当并发槽全部被占用时,尝试连接的客户端将被锁定并必须在队列中等待。
这些问题仅会在客户端较多、网络速度较慢的情况下才会显现。 通过快速本地网络对单个客户端进行基准测试时,它们不会出现。
有几个原因导致您可能无法在基准测试中看到这些影响。
请注意,大多数基准测试工具仅报告成功的交易。 由于资源耗尽而停滞的连接可能不会被报告,或者可能仅占成功连接的一小部分。 这掩盖了现实交通问题的本质。
任何基于线程或进程的 Web 或应用服务器都容易受到并发限制。
任何为每个连接分配一个线程或进程的 Web 或应用平台都固有这个问题。 在优化的基准测试环境中,它并不容易被检测到,但在实际环境中,它会表现为性能不佳和 CPU 利用率过高。
您可以采取几种措施来解决此问题:
NGINX 使用不同的架构,不会受到上述并发问题的影响。 它将缓慢的客户端连接转换为优化的基准连接,以从您的服务器中提取最佳性能。
NGINX 使用高效的事件驱动模型来管理连接。
每个NGINX进程可以同时处理多个连接。 当接受新的连接时,开销非常低(包括一个新的文件描述符和一个要轮询的新事件),与上面描述的每个进程或每个线程模型不同。 NGINX 有一个非常有效的事件循环:
这使得每个 NGINX 进程能够轻松同时扩展到数万、数万或数十万个连接。
然后,NGINX 使用本地保持连接池将请求代理到上游服务器。 您不会因打开和关闭 TCP 连接而产生开销,并且 TCP 堆栈会快速适应最佳窗口大小和重试参数。 通过本地优化的网络,写入请求和读取响应要快得多:
最终效果是上游服务器发现自己通过快速网络与单个本地客户端(NGINX)通信,并且该客户端充分利用 HTTP 保持活动连接,以最大限度地减少连接设置,而无需不必要地保持连接打开。 这会使服务器恢复到其最佳的、类似基准的环境。
使用 NGINX 充当 HTTP 代理,您会看到:
消除 HTTP 繁重的负担只是 NGINX 能够为超载的应用基础设施带来的性能转换措施之一。
NGINX 的HTTP 缓存功能可以缓存来自上游服务器的响应,并遵循标准缓存语义来控制缓存的内容和缓存时间。 如果多个客户端请求相同的资源,NGINX 可以从其缓存中做出响应,而不会给上游服务器带来重复请求的负担。
NGINX 还可以卸载上游服务器的其他操作。 您可以卸载数据压缩操作以减少带宽使用,集中SSL/TLS 加密和解密,执行初始客户端身份验证(例如,使用HTTP 基本身份验证、对外部身份验证服务器的子请求和JSON Web 令牌),并在必要时应用各种规则来限制流量。
最后,不要忘记,与其他加速代理、负载均衡器或应用交付控制器(ADC)不同,NGINX 也是一个完整的 Web 服务器。 您可以使用 NGINX提供静态内容,将流量转发到 Java、PHP、Python、Ruby 和其他语言的应用服务器,传送媒体(音频和视频) ,与身份验证和安全系统集成,甚至使用嵌入在 NGINX 配置中的规则直接响应事务。
NGINX 和 NGINX Plus 没有内置性能限制,可以充分利用您现在和将来部署它的硬件。
要试用 NGINX Plus,请立即开始30 天免费试用或联系我们讨论您的用例。
“这篇博文可能引用了不再可用和/或不再支持的产品。 有关 F5 NGINX 产品和解决方案的最新信息,请探索我们的NGINX 产品系列。 NGINX 现在是 F5 的一部分。 所有之前的 NGINX.com 链接都将重定向至 F5.com 上的类似 NGINX 内容。”