选择NGINX Plus负载均衡技术

NGINX | 2015年10月29日

我们已撰写了大量文章,介绍如何使用NGINX Plus和NGINX Open Source对网站和应用进行负载均衡,以优化可用性和可靠性。负载均衡是实现以下目标的基础工具:提升应用性能、大规模交付应用,以及部署容器和微服务。

我们此前介绍过如何部署NGINX Plus,包括部署在数据中心(可能与传统应用交付控制器配合使用)、在容器中以及在云环境中,例如Amazon Web Services(AWS)、Google Cloud Platform和Microsoft Azure。

本文将重点介绍NGINX Plus和NGINX中的负载均衡技术(也称为负载均衡方法或算法),并就如何针对不同用例选择合适的方法提供一些建议。NGINX提供4种负载均衡技术(轮询、哈希、IP哈希和最少连接),NGINX Plus还增加了1种(最短时间)。除IP哈希外,所有用于HTTP流量的方法也适用于TCP(NGINX Plus Release 9及更高版本还支持UDP)。

[编者:NGINX Plus R16和NGINX Open Source 1.15.1新增了“二选一随机”负载均衡算法。有关该算法的讨论,请参阅博客文章NGINX 与“二选一的力量”负载均衡算法。]

回顾负载均衡技术

本文假设您已了解负载均衡配置的基础知识。如需复习,可以查看以下资源:

  • 以下文档中的文章提供了完整概述:NGINX Plus 管理指南。
  • 高性能负载均衡链接至有关NGINX Plus增强功能的详细说明,这些功能可进一步提高负载均衡方法的效率。
  • 使用NGINX和NGINX Plus实现负载均衡,第1部分和第2部分通过分步讲解,展示如何利用NGINX Plus的增强功能,将简单的反向代理构建为全面的负载均衡解决方案。
  • The 负载均衡解决方案页面提供其他资源的链接,包括电子书、网络研讨会和白皮书。

为简便起见,本文将重点介绍HTTP负载均衡,其配置位于http上下文中。TCP负载均衡则配置在stream上下文中(NGINX Plus Release 9及更高版本中的UDP负载均衡也是如此)。虽然HTTP和TCP/UDP负载均衡器的功能相同,但由于协议本身存在差异,可用的指令和参数也有所不同。有关详细信息,请参阅关于HTTP和TCP/UDP的Upstream模块文档。

使用两个配置块即可启用负载均衡。下面将展示其基本形式,不包含可选参数或任何辅助功能:

  • The server块定义一个虚拟服务器,用于侦听符合指定特征的流量,并将其代理到一个命名的上游服务器组。在示例中,该虚拟服务器通过默认端口(80)侦听发送到www.example.com的HTTP流量,并将其代理到名为backend的上游服务器组。所有示例均使用相同的此配置块。
server {    server_name www.example.com;

    location / {
       proxy_pass http://backend;
    }
}
  • (NGINX Plus和NGINX还可以对FastCGI、memcached、SCGI和uwsgi后端服务器进行负载均衡。请将proxy_pass替换为相应的指令:fastcgi_pass、memcached_pass、scgi_pass或uwsgi_pass。)
  • The upstream块用于命名上游组,并列出其中的服务器。服务器可通过主机名、IP地址或UNIX域套接字路径来标识。在示例中,名为backend的上游组包含3台服务器:web1、web2和web3。负载均衡技术在upstream块中指定,因此后续各节将重点介绍这部分内容。以下是默认轮询方法的配置块示例:
upstream backend {    server web1;
    server web2;
    server web3;
}

轮询

轮询是NGINX Plus和NGINX的默认负载均衡技术。负载均衡器按顺序遍历上游服务器列表,依次将下一个连接请求分配给每台服务器。

对于以下backend上游组配置示例,负载均衡器会将前3个连接请求依次发送到web1、web2和web3,将第4个请求发送到web1,将第5个请求发送到web2,依此类推。

upstream backend {    server web1;
    server web2;
    server web3;
}

server {
    server_name www.example.com;

    location / {
       proxy_pass http://backend;
    }
}

哈希

使用哈希方法时,负载均衡器会针对每个请求,根据指定的文本与NGINX变量组合计算哈希值,并将该哈希值与其中一台服务器关联。具有该哈希值的所有请求都会发送到这台服务器,因此,此方法可实现一种基本的会话持久性。

在以下示例中,hash指令以请求的协议(http或https)和完整URI作为哈希计算依据:

upstream backend {    hash $scheme$request_uri;

    server web1;
    server web2;
    server web3;
}

server {
    server_name www.example.com;

    location / {
       proxy_pass http://backend;
    }
}

IP 哈希

IP Hash(仅适用于HTTP)是Hash方法的预定义变体,其哈希值基于客户端的IP地址。可使用ip_hash指令进行设置。

upstream backend {    ip_hash;

    server web1;
    server web2;
    server web3;
}

server {
    server_name www.example.com;

    location / {
       proxy_pass http://backend;
    }
}

如果客户端使用IPv6地址,则基于完整地址计算哈希值。如果使用IPv4地址,则仅基于地址的前3个八位字节计算哈希值。此设计旨在针对由ISP从子网(/24)范围内动态分配IP地址的客户端进行优化。重启或重新连接时,客户端地址通常会变为/24网络范围内的另一个地址,但该连接仍来自同一客户端,因此无需更改其与服务器的映射关系。

但是,如果网站的大部分流量来自同一/24网络中的客户端,则不适合使用IP Hash,因为它会将所有客户端映射到同一台服务器。在这种情况下(或者,如果出于其他原因需要对全部4个八位字节进行哈希计算),请改用Hash方法并指定$remote_addr变量。

hash $remote_addr;

最少连接

使用最少连接方法时,负载均衡器会比较当前与各服务器建立的活动连接数,并将请求发送到连接数最少的服务器。可使用least_conn指令进行配置。

upstream backend {
    least_conn;

    server web1;
    server web2;
    server web3;
}

server {
    server_name www.example.com;

    location / {
       proxy_pass http://backend;
    }
}

最短时间

使用Least Time方法(仅在NGINX Plus中提供)时,负载均衡器会通过数学运算,将每台服务器的两个指标(当前活动连接数和历史请求响应时间的加权平均值)合并计算,并将请求发送到计算值最低的服务器。

在least_time指令中选择的参数决定跟踪以下哪一种响应时间:接收响应标头所需的时间(header),或接收完整响应所需的时间(last_byte)。

upstream backend {    least_time (header | last_byte);

    server web1;
    server web2;
    server web3;
}

server {
    server_name www.example.com;

    location / {
       proxy_pass http://backend;
    }
}

注意事项:

  • 对于TCP和UDP负载均衡(在stream上下文中),可通过为least_time指令指定以下参数,从三种响应时间类型中进行选择:
    • connect,连接到上游服务器所需的时间
    • first_byte,接收响应数据第一个字节所需的时间
    • last_byte:接收响应数据最后一个字节所需的时间
  • 对于HTTP和TCP/UDP流量,在NGINX Plus R12及更高版本中,添加inflight参数可在各项指标中纳入未完成的连接;早期版本默认纳入此类连接。

选择负载均衡技术

那么,如何判断哪种负载均衡技术更适合您的网站或应用?

不同网站的流量模式差异很大,即使是同一网站,在一天中的不同时段也会有所不同。因此,仅根据单一特征(例如突发流量与稳定流量、短连接与长连接等)来选择负载均衡技术并不合理。不过,我们将分析每种方法的优缺点,帮助您缩小选择范围。

运行测试以比较不同方法

无论考虑采用哪些负载均衡方法,都建议进行测试,以确定哪种方法更适合您的流量。“更适合”通常是指向客户端返回响应所需的时间最短,但您也可能采用其他标准。

应用性能管理工具非常适合此类测试。您可以创建自定义界面,为上游组中的每台服务器显示图表,以便在测试期间随着数值变化进行实时比较。多种APM为NGINX Plus和NGINX提供自定义插件,包括AppDynamics、Datadog、Dynatrace和New Relic。

如果所有服务器的容量相同,测试最为简单。否则,需要设置服务器权重,使容量更大的服务器接收更多请求。请参阅下文的“服务器配置不同时设置权重”。

测试期间需要检查的一些指标包括:

  • CPU和内存负载:查看CPU和内存占总容量的使用百分比。如果所有服务器的负载不均衡,则流量分配效率不高。
  • 服务器响应时间:如果某些服务器的响应时间始终高于其他服务器,说明“负载较重”的请求(需要更多计算,或需要调用数据库或其他服务)正以不均衡的方式定向到这些服务器。请尝试调整权重,因为这种不均衡可能是权重设置不正确所致,而非负载均衡技术本身存在问题。
  • 响应客户端的总时间:同样,如果某些服务器的响应时间持续较长,说明这些服务器可能分配到了过多耗时请求。可以尝试调整权重,看看能否解决此问题。
  • 错误和失败的请求:需要确保测试期间的失败请求和其他错误数量不高于网站的正常水平。否则,决策依据将是错误状况,而非实际流量。对于某些错误,服务器发送响应的速度可能比请求成功时更快。例如,对于HTTP响应代码404(File Not Found),服务器返回错误的速度可能远快于文件实际存在时交付该文件的速度。使用最少连接数和最短时间负载均衡算法时,这可能导致负载均衡器偏向实际上运行状况不佳的服务器。

优缺点和使用场景

下面,我们来看看每种负载均衡技术的优缺点,并介绍它们特别适用的一些用例。我们将按照对大多数用例的适用程度由低到高依次讨论。先简单概括一下:我们认为,Least Connections(对于NGINX Plus,则还包括Least Time)适用于更广泛的用例。

哈希和IP哈希

Hash和IP Hash负载均衡技术会在特定类型的客户端请求(以哈希值表示)与某台服务器之间建立固定关联。这也称为会话持久性,即具有相同哈希值的所有请求始终发送到同一台服务器。

这些方法的主要缺点是,无法保证将请求等量分配到各服务器,更不用说均衡负载了。哈希算法会将所有可能的哈希值均匀划分到多个“桶”中,上游组中的每台服务器对应一个桶,但无法预测实际请求的哈希值是否会均匀分布。例如,假设有10个客户端正在访问某个站点,而IP Hash算法恰好将其中7个IP地址的哈希值关联到web1,将1个关联到web2,将2个关联到web3。最终,web1服务器收到的请求数会超过其他服务器请求数总和的两倍。

Hash和IP Hash负载均衡技术可能导致负载分布不均

因此,当保持会话的益处超过负载不均衡可能带来的不利影响时,使用Hash或IP Hash是合理的。它们是NGINX中仅有的会话持久性机制。NGINX Plus还提供另外三种更为复杂的会话持久性机制,可与实际负载均衡结合使用(可通过sticky指令进行配置)。不过,即使使用NGINX Plus,也可能会选择Hash或IP Hash,因为这三种机制不适用于以下情况:

  • 浏览器或客户端应用不接受Cookie,并且应用无法在没有Cookie的情况下使用会话持久性机制。请使用IP Hash方法,将每个客户端(具体而言,是其IP地址)与特定服务器关联。
  • 如果希望将指定URL的请求始终发送到同一台服务器,以利用服务器自身的缓存,请结合$request_uri变量使用Hash方法,确保每次都从同一台服务器获取文件。例如,假设提供某个.php文件需要执行多次耗时的数据库调用,但获取的数据不经常变化,因此可以缓存。如果将该文件的所有请求都定向到同一台服务器,则只有第一个客户端会因数据库调用而经历较长延迟。后续客户端都可以从缓存中快速获取数据。另一个优势是,只需由这一台服务器缓存这组特定数据。由于无需在每台服务器上重复缓存相同数据,因此可以使用容量更小的缓存。

在以下几种情况下,IP Hash以及键中包含客户端IP地址的Hash无法正常工作:

  • 当客户端的IP地址可能在会话期间发生变化时,例如移动客户端从WiFi网络切换到蜂窝网络。
  • 当大量客户端的请求通过正向代理传输时,因为这些请求都使用该代理的IP地址。

哈希具有确定性(哈希算法每次都会生成相同的结果)。这会带来两个积极的附带效果:部署中的所有NGINX Plus或NGINX实例都会以完全相同的方式对请求进行负载均衡,并且哈希到服务器的映射在负载均衡器重启后仍然保持不变。(实际上,该映射会在重启后重新计算,但由于结果始终相同,因此实际上相当于保持不变。)

另一方面,更改上游服务器集合通常会导致至少部分映射被重新计算,从而中断会话持久性。可以通过以下方式在一定程度上减少需要重新计算的映射数量:

  • 对于Hash方法,请在consistent参数中添加到hash指令;NGINX Plus使用ketama哈希算法,从而减少重新映射。
  • 对于IP哈希方法,在暂时从上游组中移除服务器之前,请在该服务器的down参数添加到其server指令中,如以下示例中的web2所示。系统不会重新计算映射,因为假定该服务器很快会恢复服务。
upstream backend {    ip_hash;

    server web1;
    server web2 down;
    server web3;
}

轮询

如前所述,轮询是NGINX Plus和NGINX的默认负载均衡方法。因此,它无疑是最容易选择的方法,除了上游组本身,无需进行任何其他配置。

普遍认为,当服务器和请求的特征不太可能导致某些服务器比其他服务器负载更高时,轮询法的效果更好。适用条件包括:

  • 所有服务器的容量大致相同。如果服务器权重能够准确反映服务器之间的差异,则此要求的重要性会降低。
  • 所有服务器均托管相同的内容。
  • 各个请求所需的处理时间或处理能力大致相同。如果请求负载差异很大,负载均衡器可能会在短时间内将大量高负载请求发送到同一台服务器,导致该服务器过载。
  • 流量并不会大到经常使服务器接近满负载。如果服务器已经处于高负载状态,轮询算法机械地分配请求,就更有可能使部分服务器不堪重负,出现上一条所述的过载情况。

轮询方法尤其适合测试场景,因为它可确保请求以相同数量(或按适当的权重比例)分配到所有服务器。其他一些方法在流量较低时并不总能均匀分配流量,可能导致测试结果出现偏差。

这种均匀分配方式还可以反映缓存是否运行良好并达到满负载:由于轮询无法将特定文件的请求发送到同一台服务器,因此每台服务器最终都可能会提供并缓存各种文件(通常还会缓存许多与其他服务器相同的文件),从而更容易填满缓存。

最后,初始的均匀分配有助于发现NGINX Plus中的会话持久性问题(通过sticky指令配置)。

最少连接和最短时间

如上所述,最少连接是适用场景较广的负载均衡技术,尤其适合生产流量。客户的实际反馈也印证了这一点。其性能稳定且可预测。

最少连接还可根据服务器容量有效分配工作负载。性能更强的服务器处理请求的速度更快,因此在任何给定时刻,与容量较小的服务器相比,其仍在处理(甚至仍在等待开始处理)的连接数可能更少。最少连接会将每个请求发送到当前连接数最少的服务器,因此更有可能将请求发送到性能更强的服务器。(不过,设置权重仍可进一步提高请求分配效率,具体如下文服务器配置不同时设置权重所述。)

可以将Least Time(仅限NGINX Plus)视为更敏感的Least Connections。通过纳入平均响应时间,该算法会考虑服务器近期的性能历史记录(实际上采用的是指数加权移动平均值,因此较早的响应时间对平均值的影响小于较近的响应时间)。

当上游服务器的平均响应时间差异很大时,Least Time尤其适用。例如,如果出于灾难恢复目的在不同数据中心部署了服务器,Least Time往往会将更多请求发送到本地服务器,因为这些服务器的响应速度更快。另一个用例是云环境,其中的服务器性能通常很难预测。

Least Time是NGINX Plus中的一种负载均衡技术

服务器配置不同时设置权重

我们已多次提到,当上游组中的服务器容量不同时,设置服务器权重非常重要。这对于轮询负载均衡器尤为重要,否则它会向每台服务器发送相同数量的请求。这很可能导致性能较低的服务器过载,而性能较高的服务器却有部分资源闲置。

要设置权重,请在weight参数添加到server指令中(可添加到一个或多个),这些指令位于upstream块中。默认值为1。

可以按以下方式理解为不同负载均衡技术设置权重所产生的影响。请注意,这些说明在概念上是正确的,但NGINX Plus代码中的实现不一定使用所述的数学运算。以下是示例所用的上游组:

upstream backend {    server web1 weight=6;
    server web2 weight=3;
    server web3;
}
  • 轮询:每台服务器收到的传入请求比例,等于其权重除以所有服务器的权重总和。在本例中,每10个请求中,web1收到6个(60%),web2收到3个(30%),web3收到1个(10%)。
  • 哈希和IP 哈希:请回顾一下,在未设置权重时,哈希算法会将所有可能的哈希值均匀划分为多个“桶”,上游组中的每台服务器对应一个桶。设置权重后,算法会先计算权重总和,再按该总数将所有可能的哈希值划分到相应数量的桶中,并根据每台服务器的权重为其分配相同数量的桶。在本例中,共有10个桶,每个桶包含10%的可能哈希值。其中6个桶(占可能哈希值的60%)分配给web1,3个桶(30%)分配给web2,1个桶(10%)分配给web3。
  • 最少连接和最短时间:前文提到,即使不设置权重,这些算法也能根据服务器容量有效地在各服务器之间分配工作负载。设置权重后,这方面的效果会进一步提升。最少连接和最短时间算法会将每个请求发送到“得分”最低的服务器。两种算法的得分分别为连接数,以及连接数与时间的数学组合。设置权重后,负载均衡器会将每台服务器的得分除以其权重,然后将请求发送到所得值最低的服务器。以下示例使用示例权重和所示的活动连接数来说明最少连接算法:web1的得分最低,为100,因此会收到该请求,尽管其连接数为600,是web2的1.5倍,并且是web3的4倍以上。
    • web1:600 个活动连接 ÷ 6 = 100
    • web2:400 个活动连接 ÷ 3 = 133
    • web3:125 个活动连接 ÷ 1 = 125

总结

在评估NGINX Plus和NGINX所提供的负载均衡技术的优缺点后,我们认为Least Connections(对于NGINX Plus,还包括Least Time)适用于较广泛的用例。不过,请务必在实际部署中测试多种方法,因为流量与服务器特性的独特组合可能意味着其他方法更适合您的环境。

要亲自试用NGINX Plus负载均衡,请立即开始30天免费试用,或联系我们讨论您的用例。

欢迎分享您在不同用例中使用负载均衡的经验。请在下方评论区留言。


分享

相关博客文章

NGINX Plus R26 正式发布
NGINX | 2022/02/15

NGINX Plus R26 正式发布

NGINX Plus R26通过缓存JSON Web密钥集加快JWT验证,并强化TLS握手,从而提升安全性。NGINX JavaScript的新功能包括增强对异步函数的支持,以及实现WebCrypto API。

使用NGINX App Protect DoS保护gRPC应用,抵御严重的DoS攻击
NGINX | 2022/02/10

使用NGINX App Protect DoS保护gRPC应用,抵御严重的DoS攻击

gRPC具备开发人员设计和部署现代应用所需的速度与灵活性,但其开放性也使其极易受到第7层DoS攻击。NGINX App Protect DoS可保护gRPC应用免受此类破坏性攻击。

Ingress Controller 选型指南(第 2 部分):风险与面向未来的规划
NGINX | 2021/09/13

Ingress Controller 选型指南(第 2 部分):风险与面向未来的规划

了解选择错误的Ingress控制器可能带来的风险,以及如何从关键方面着手,让您的选择更好地适应未来需求。

NGINX Plus R19 正式发布
NGINX | 2019/09/03

NGINX Plus R19 正式发布

NGINX Plus R19扩展了监控功能,不仅提供更多指标,还新增了指标分析方式;增加试运行模式,用于测试限速效果;增强键值存储,以支持IP地址范围;并使限速更加动态。

NGINX现已正式加入F5
NGINX | 2019/05/09

NGINX现已正式加入F5

NGINX现已正式成为F5的一部分。Gus Robertson现任F5 NGINX业务部门负责人。他阐述了两家公司在人才、文化和愿景方面融合的价值,以及双方将如何携手增强NGINX的开源产品和商业产品。

使用NGINX和NGINX Plus实现高性能缓存
NGINX | 2016/08/24

使用NGINX和NGINX Plus实现高性能缓存

我将介绍NGINX在内容缓存和性能方面的工作原理,并分享一些控制NGINX内容缓存方式的技巧。