高性能负载均衡: 分类&架构&算法 ############################## .. note:: 高性能集群的本质很简单,通过增加更多的服务器来提升系统整体的计算能力。由于计算本身存在一个特点:同样的输入数据和逻辑,无论在哪台服务器上执行,都应该得到相同的输出。因此高性能集群设计的复杂度主要体现在任务分配这部分,需要设计合理的任务分配策略,将计算任务分配到多台服务器上执行。 .. important:: 高性能集群的复杂性主要体现在需要『增加一个任务分配器』,以及为任务『选择一个合适的任务分配算法』。 对于任务分配器,现在更流行的通用叫法是 “负载均衡器”:: 任务分配并不只是考虑计算单元的负载均衡,不同的任务分配算法目标是不一样的 1. 有的基于负载考虑 2. 有的基于性能(吞吐量、响应时间)考虑 3. 有的基于业务考虑 负载均衡分类 ============ 常见的负载均衡系统包括 3 种:: 1. DNS 负载均衡 2. 硬件负载均衡 3. 软件负载均衡 DNS 负载均衡 ------------ 优点:: 1. 简单、成本低 负载均衡工作交给 DNS 服务器处理,无须自己开发或者维护负载均衡设备 2. 就近访问,提升访问速度 DNS 解析时可以根据请求来源 IP,解析成距离用户最近的服务器地址,可以加快访问速度,改善性能 缺点:: 1. 更新不及时 DNS 缓存的时间比较长,修改 DNS 配置后,由于缓存的原因,还是有很多用户会继续访问修改前的 IP 这样的访问会失败,达不到负载均衡的目的,并且也影响用户正常使用业务 2. 扩展性差 DNS 负载均衡的控制权在域名商那里,无法根据业务特点针对其做更多的定制化功能和扩展特性 3. 分配策略比较简单 DNS 负载均衡支持的算法少 不能区分服务器的差异(不能根据系统与服务的状态来判断负载) 也无法感知后端服务器的状态 .. note:: 针对 DNS 负载均衡的一些缺点,对于时延和故障敏感的业务,有一些公司自己实现了 HTTP-DNS 的功能,即使用 HTTP 协议实现一个私有的 DNS 系统。这样的方案和通用的 DNS 优缺点正好相反 硬件负载均衡 ------------ 目前业界典型的硬件负载均衡设备有两款:: F5 和 A10 优点:: 1. 功能强大 全面支持各层级的负载均衡,支持全面的负载均衡算法,支持全局负载均衡。 2. 性能强大 对比一下,软件负载均衡支持到 10 万级并发已经很厉害了,硬件负载均衡可以支持 100 万以上的并发。 3. 稳定性高 商用硬件负载均衡,经过了良好的严格测试,经过大规模使用,稳定性高。 4. 支持安全防护 硬件均衡设备除具备负载均衡功能外,还具备防火墙、防 DDoS 攻击等安全功能。 缺点:: 1. 价格昂贵 2. 扩展能力差 硬件设备,可以根据业务进行配置,但无法进行扩展和定制 软件负载均衡 ------------ 常见的有 Nginx 和 LVS:: Nginx 是软件的 7 层负载均衡 LVS 是 Linux 内核的 4 层负载均衡 软件和硬件的最主要区别就在于性能:: 硬件负载均衡性能远远高于软件负载均衡性能。 Nginx 的性能是万级,一般的 Linux 服务器上装一个 Nginx 大概能到 5 万 / 秒; LVS 的性能是十万级,据说可达到 80 万 / 秒; 而 F5 性能是百万级,从 200 万 / 秒到 800 万 / 秒都有 优点:: 简单:无论是部署还是维护都比较简单。 便宜:只要买个 Linux 服务器,装上软件即可。 灵活:4 层和 7 层负载均衡可以根据业务进行选择; 也可以根据业务进行比较方便的扩展,例如,可以通过 Nginx 的插件来实现业务的定制化功能 缺点(与硬件负载均衡相比):: 性能一般:一个 Nginx 大约能支撑 5 万并发 功能没有硬件负载均衡那么强大 一般不具备防火墙和防 DDoS 攻击等安全功能 负载均衡典型架构 ================ 组合的基本原则为:: 1. DNS 负载均衡用于实现地理级别的负载均衡; 2. 硬件负载均衡用于实现集群级别的负载均衡; 3. 软件负载均衡用于实现机器级别的负载均衡。 .. image:: https://img.zhaoweiguo.com/knowledge/images/architectures/performances/slb-type1.png 负载均衡算法 ============ 算法大体上可以分为下面几类:: 1. 任务平分类 负载均衡系统将收到的任务平均分配给服务器进行处理 这里的 “平均” 可以是绝对数量的平均,也可以是比例或者权重上的平均 2. 负载均衡类 负载均衡系统根据服务器的负载来进行分配 这里的负载并不一定是通常意义上我们说的 “CPU 负载”,而是系统当前的压力 可以用 CPU 负载来衡量,也可以用连接数、I/O 使用率、网卡吞吐量等来衡量系统的压力 3. 性能最优类 负载均衡系统根据服务器的响应时间来进行任务分配,优先将新任务分配给响应最快的服务器 4. Hash 类 负载均衡系统根据任务中的某些关键信息进行 Hash 运算 将相同 Hash 值的请求分配到同一台服务器上 常见的有源地址 Hash、目标地址 Hash、session id hash、用户 ID Hash 等 轮询 ---- .. note:: 负载均衡系统收到请求后,按照顺序轮流分配到服务器上。只要服务器在运行,运行状态是不关注的。 加权轮询 -------- .. note:: 加权轮询是轮询的一种特殊形式,其主要目的就是为了解决不同服务器处理能力有差异的问题。 负载最低优先 ------------ .. note:: 负载均衡系统将任务分配给当前负载最低的服务器,这里的负载根据不同的任务类型和业务场景, 不同的任务类型和业务场景,可以用不同的指标来衡量:: 1. LVS 这种 4 层网络负载均衡设备 可以以 “连接数” 来判断服务器的状态,服务器连接数越大,表明服务器压力越大。 2. Nginx 这种 7 层网络负载系统 可以以 “HTTP 请求数” 来判断服务器状态 (Nginx 内置的负载均衡算法不支持这种方式,需要进行扩展) 3. 如果我们自己开发负载均衡系统,可以根据业务特点来选择指标衡量系统压力。 如果是 CPU 密集型,可以以 “CPU 负载” 来衡量系统压力; 如果是 I/O 密集型,可以以 “I/O 负载” 来衡量系统压力。 负载最低优先的算法解决了轮询算法中无法感知服务器状态的问题,由此带来的代价是复杂度要增加很多:: 1. 最少连接数优先的算法要求负载均衡系统统计每个服务器当前建立的连接 其应用场景仅限于负载均衡接收的任何连接请求都会转发给服务器进行处理, 否则如果负载均衡系统和服务器之间是固定的连接池方式,就不适合采取这种算法。 例如 LVS 可以采取这种算法进行负载均衡 而一个通过连接池的方式连接 MySQL 集群的负载均衡系统就不适合 2. CPU 负载最低优先的算法要求负载均衡系统以某种方式收集每个服务器的 CPU 负载 而且要确定是以 1 分钟的负载为标准,还是以 15 分钟的负载为标准 不存在 1 分钟肯定比 15 分钟要好或者差。 不同业务最优的时间间隔是不一样的,时间间隔太短容易造成频繁波动, 时间间隔太长又可能造成峰值来临时响应缓慢。 .. note:: 负载最低优先算法基本上能够比较完美地解决轮询算法的缺点,因为采用这种算法后,负载均衡系统需要感知服务器当前的运行状态。当然,其代价是复杂度大幅上升。通俗来讲,轮询可能是 5 行代码就能实现的算法,而负载最低优先算法可能要 1000 行才能实现。负载最低优先算法如果本身没有设计好,或者不适合业务的运行特点,算法本身就可能成为性能的瓶颈,或者引发很多莫名其妙的问题。所以负载最低优先算法虽然效果看起来很美好,但实际上真正应用的场景反而没有轮询(包括加权轮询)那么多。 性能最优类 ---------- .. note:: 负载最低优先类算法是站在服务器的角度来进行分配的,而性能最优优先类算法则是站在客户端的角度来进行分配的,优先将任务分配给处理速度最快的服务器,通过这种方式达到最快响应客户端的目的。 复杂度很高,主要体现在:: 1. 需收集和分析每个服务器每个任务的响应时间,在大量任务处理的场景下,收集和统计本身也会消耗较多的性能。 2. 为了减少这种统计上的消耗,可以采取采样的方式来统计 即不统计所有任务的响应时间,而是抽样统计部分任务的响应时间来估算整体任务的响应时间。 采样统计虽然能够减少性能消耗,但使得复杂度进一步上升,因为要确定合适的采样率, a. 采样率太低会导致结果不准确, b. 采样率太高会导致性能消耗较大, 找到合适的采样率也是一件复杂的事情。 3. 无论是全部统计还是采样统计,都需要选择合适的周期: 是 10 秒内性能最优,还是 1 分钟内性能最优,还是 5 分钟内性能最优…… 没有放之四海而皆准的周期,需要根据实际业务进行判断和选择, 这也是一件比较复杂的事情,甚至出现系统上线后需要不断地调优才能达到最优设计。 Hash 类 ------- .. note:: 负载均衡系统根据任务中的某些关键信息进行 Hash 运算,将相同 Hash 值的请求分配到同一台服务器上 这样做的目的主要是为了满足特定的业务需求。例如:: 1. 源地址 Hash: 将来源于同一个源 IP 地址的任务分配给同一个服务器进行处理,适合于存在事务、会话的业务。 2. ID Hash 将某 ID 标识的业务分配到同一服务器中处理,这的 ID 一般是临时性数据的 ID(如 session id) 思考 ===== 负载均衡还包括以下处理:: 1. 集群调度平台(PaaS)平台 例如 k8s 、docker, 可以实现动态扩容和缩减,根据事实的并发量进行处理 当然前提是包括 Nginx、lvs、haproxy 前端负载均衡器不能挂掉 2. 分布式框架 例如:Spring Cloud 的 ribbion、feign 也可以从应用层面实现负载均衡 软件负载均衡中的某个机器挂了是怎么发现的呢:: 一看连接是否存在 二看是否能够发出去 三看响应是否快速