几张图带你了解负载均衡

avatar
前端工程师 @稀土

负载均衡 (Load Balancing)

Web 应用程序的增长将超过单个服务器部署。公司要么希望提高其可用性、可扩展性,要么两者兼而有之!为此,他们将应用程序部署在多个服务器上,前面有一个负载均衡器来分发传入的请求。大公司可能需要数千台运行其Web应用程序的服务器来处理负载。

在这篇文章中,我们将重点介绍单个负载均衡器将 HTTP 请求分发到一组服务器的方式。我们将从底层开始,逐步发展到现代负载平衡算法。

手动调节参数帮助更好理解PlayGround

负载均衡 (Load Balancing) 是现代计算机网络和服务器架构中至关重要的一项技术。它的作用类似于交通警察将繁忙的道路上的交通流量分流,以确保交通畅通无阻。在计算机领域,负载均衡的任务是将网络流量或应用程序请求分散到多个服务器或计算资源上,以提高性能、可用性和可伸缩性。

负载均衡的主要目标是确保每个服务器都能够高效地处理请求,避免某一台服务器过载,同时最大程度地减少服务器的空闲时间。这种平衡有助于提高系统的稳定性和性能,确保用户能够快速访问应用程序或网站,而不会受到服务器性能的限制。

负载均衡系统中,有多种不同的算法和策略可供选择,以决定如何将流量分发给不同的服务器。这些算法可以基于不同的因素,如服务器的负载状况、响应时间、服务器的地理位置等来进行决策。这些决策使得负载均衡系统能够智能地调整流量分配,以适应不同的工作负载情况。

总之,负载均衡是现代计算机网络和云计算中的关键组成部分,它通过合理地分配流量和请求,提高了系统的可用性和性能,确保了用户能够获得更快、更可靠的服务。无论是网站、应用程序还是云基础设施,负载均衡都扮演着至关重要的角色,有助于保持互联网的畅通无阻。

问题起源

让我们从头开始:单个负载均衡器向单个服务器发送请求。请求以每秒 1 个请求 (RPS) 的速率发送,并且每个请求的大小随着服务器处理它而减小。

屏幕录制2023-09-26 14.30.11.gif

现代服务器功能强大,可以处理大量请求。但是当他们跟不上时会发生什么?

屏幕录制2023-09-26 14.34.29.gif

在这里,我们看到 3 RPS 的速率会导致一些请求丢弃。如果一个请求在处理另一个请求时到达服务器,服务器将丢弃它。这将导致向用户显示错误,这是我们想要避免的。我们可以将另一台服务器添加到负载均衡来解决此问题。

屏幕录制2023-09-26 14.36.12.gif 依次向每个服务器发送请求,称为“轮询”负载平衡。它是最简单的负载平衡形式之一,当您的服务器都同样强大并且您的请求都同样昂贵时,它运行良好。

屏幕录制2023-09-26 14.44.18.gif

当 round robin 解决不了问题时

在现实世界中,服务器很少有同样强大并且请求同样昂贵的情况。即使您使用完全相同的服务器硬件,性能也可能有所不同。应用程序可能必须为许多不同类型的请求提供服务,这些请求可能具有不同的性能特征。

让我们看看当我们改变请求成本时会发生什么。在下面的模拟中,请求的开销并不相同。您可以通过某些请求比其他请求收缩更长的时间看到这一点。

屏幕录制2023-09-26 14.57.38.gif

虽然大多数请求都能成功处理,但我们确实会丢弃一些请求。我们可以缓解此问题的方法之一是使用“请求队列”

屏幕录制2023-09-26 15.01.23.gif 请求队列帮助我们处理不确定性,但这是一种权衡。我们将丢弃更少的请求,但代价是某些请求具有更高的延迟。

屏幕录制2023-09-26 15.13.23.gif 它们处理请求的时间越长,它们的颜色变化就越大。您还会注意到,由于请求成本差异,服务器开始表现出不平衡。队列将备份在倒霉的服务器上,并且必须连续处理多个昂贵的请求。如果队列已满,请求会被丢弃。

上述所有内容同样适用于功率不同的服务器。在下面的模拟中,我们还改变了每个服务器的功能,这些服务器在视觉上用较深的灰色阴影表示。

屏幕录制2023-09-26 15.18.15.gif

服务器被赋予一个随机的功率值,但有些服务器的功能不如其他服务器强大,并且很快就会开始丢弃请求。同时,功能更强大的服务器大部分时间都处于空闲状态。此场景显示了循环的关键弱点:方差

然而,尽管存在缺陷,但循环调度仍然是nginx的默认HTTP负载平衡方法。

改进循环调度

可以调整循环以在方差下表现更好。有一种称为加权轮询算法,它涉及让人类用权重标记每个服务器,该权重指示向其发送多少请求。

在此模拟中,我们使用每个服务器的已知功率值作为其权重,并在循环访问它们时为功能更强大的服务器提供更多请求。

屏幕录制2023-09-26 15.26.58.gif 虽然这比普通轮循机制更好地处理了服务器功率的差异,但我们仍然需要应对请求差异。在实践中,让人类用手设置重量很快就会崩溃。将服务器性能降低到一个数字是很困难的,并且需要对实际工作负载进行仔细的负载测试。这种情况很少发生,因此加权循环的另一个变体使用代理指标动态计算权重:延迟

按理说,如果一台服务器处理请求的速度比另一台服务器快 3 倍,那么它的速度可能快 3 倍,并且应该比另一台服务器接收的请求多 3 倍。

屏幕录制2023-09-26-15.28.51.gif 这次我向每台服务器添加了文本,显示最近处理的 3 个请求的平均延迟。然后,我们根据延迟的相对差异决定是否向每个服务器发送 1、2 或 3 个请求。结果与初始加权循环模拟非常相似,但无需预先指定每个服务器的权重。该算法还将能够适应服务器性能随时间的变化。这称为“动态加权循环”。

让我们看看它如何处理服务器功率和请求成本差异很大的复杂情况。以下模拟使用的随机值

屏幕录制2023-09-26-15.34.22.gif

不考虑循环调度呢?

动态加权轮循机制似乎很好地解释了服务器功率和请求成本的差异。但是,如果我告诉你我们可以做得更好,并且使用更简单的算法呢?

屏幕录制2023-09-26-15.36.29.gif 这称为“最少连接”负载平衡。

由于负载平衡器位于服务器和用户之间,因此它可以准确地跟踪每个服务器有多少未完成的请求。然后,当收到新请求并且需要确定将其发送到何处时,它知道哪些服务器要做的工作量最少,并确定这些服务器的优先级。

无论存在多少方差,该算法的性能都非常好。它通过保持对每个服务器正在做什么的准确理解来消除不确定性。它还具有易于实现的好处。

让我们在类似的复杂模拟中看到这一点,这与我们上面给出的动态加权循环算法的参数相同。同样,这些参数在给定范围内是随机的.

屏幕录制2023-09-26 15.40.47.gif 虽然此算法在简单性和性能之间取得了很好的平衡,但它也不能幸免于丢弃请求。但是,您会注意到,此算法丢弃请求的唯一时间是实际上没有更多可用队列空间时。它将确保所有可用资源都在使用中,这使其成为大多数工作负载的绝佳默认选择。

优化延迟

到目前为止,我一直在回避讨论的一个关键部分:我们正在优化什么。隐含地,我一直认为丢弃的请求真的很糟糕,并试图避免它们。这是一个很好的目标,但这不是我们最想在 HTTP 负载均衡器中优化的指标。

我们通常更关心的是延迟。这是以毫秒为单位,从创建请求到请求得到处理的那一刻。当我们在这种情况下讨论延迟时,通常会谈论不同的“百分位数”。例如,第 50 个百分位数(也称为“中位数”)定义为毫秒值,其中 50% 的请求低于,50% 的请求高于该毫秒。

我用相同的参数运行了 3 次模拟 60 秒,每秒进行各种测量。每个模拟仅因所使用的负载平衡算法而异。让我们比较一下 3 个模拟中每个模拟的中位数: image.png 您可能没有预料到,但轮循机制具有最佳的中值延迟。如果我们不查看任何其他数据点,我们就会错过完整的故事。让我们来看看第 95 和第 99 个百分位数。

image.png 注意:每种负载平衡算法的不同百分位数之间没有颜色差异。图表上的百分位数越高,百分位数越高。

我们看到循环赛在较高的百分位数中表现不佳。循环赛怎么可能有一个很好的中位数,但糟糕的 95 和 99 百分位数?

在轮循机制中,不考虑每个服务器的状态,因此您将收到大量发送到空闲服务器的请求。这就是我们获得第 50 个百分位数低的方式。另一方面,我们也很乐意将请求发送到过载的服务器,因此第 95 和第 99 个百分位数是错误的。

我们可以以直方图形式查看完整数据: image.png 我为这些模拟选择了参数,以避免丢弃任何请求。这保证了我们比较所有 3 种算法相同数量的数据点。让我们再次运行模拟,但要增加 RPS 值,旨在推动所有算法超出它们可以处理的范围。以下是一段时间内丢弃的累积请求的图表。

image.png 最少连接可以更好地处理过载,但这样做的成本略高于第 95 和第 99 个百分位延迟。根据您的用例,这可能是一个值得的权衡。

peak exponentially weighted moving average (峰值指数加权移动平均线)

还有一个算法(PEWMA) 有兴趣的同学可以继续阅读PEWMA

结语

作者原文使用canvas写了不同场景的动画帮助读者更好的理解 感兴趣的JYM点击下方链接直达作者原文链接