负载均衡的算法
负载均衡的算法很多,而且可以根据一些业务特性进行定制化开发。抛开细节上的一些差异,根据算法所期望达到的目的,大体上可以分为以下几种负载均衡算法。
- 任务分配类:负载均衡系统将接收到的任务平均分配给每个服务并进行处理,这里的平等可以是绝对的平均,也可以是比例或者权重上的平均。
- 负载均衡类:负载均衡系统根据服务器的负载能力进行分配。这里的负载并不一定是通常意义上的 CPU 负载,而是通过系统当前的压力来衡量,可以用 CPU 负载、连接数、IO 使用率、网卡吞吐量等指标。
- 性能最优:负载均衡系统根据服务器的响应时间来进行任务的分配,优先将新任务分配给响应时间最短(最快)的服务器。
- Hash 类:负载均衡系统根据请求中的某些关键字信息进行 hash 运算,将算出来的相同 hash 值的请求分发给同一台服务器。例如:目标地址 hash、session id hash、原地址 hash、用户 id(userId) hash 等方式来计算 hash 值。

常用负载均衡算法
轮询
负载均衡系统接收到请求后,按照一定顺序将请求分发给服务器上。轮询是一种简单的负载均衡算法策略,不会去关注服务器状态。
- 优点:如果服务器都是正常的,那么轮询是最理想的,因为它会使得每个服务都得到相等量的请求,可以用'雨露均沾'来形容。
- 缺点:理想状态下轮询表现良好,但现实往往骨感。线上系统常出现问题,比如当有一台服务器挂了,轮询算法不会管服务器状态,会导致大量请求到一台已经挂掉的服务器上,从而导致系统不可用,进而造成用户流失。另外一种常见的问题是有的服务器响应快,有的响应慢(比如 32 核的服务器和 16 核的服务器),轮询算法也不关注响应快慢,所以会导致很多服务请求响应时间慢,简单导致用户体验不好,由于响应时间慢甚至可能拖垮其他系统。
加权轮询
负载均衡系统根据服务器权重进行请求任务分派到对应的服务器上,这里的权重一般是根据系统硬件配置进行静态配置的,采用动态的方式计算会更加适合业务,但是复杂度相比简单的轮询就高很多。
加权轮询是轮询的一种特殊方式,主要目的是解决服务器处理能力的差异问题。比如:集群中有的服务器是 32 核,有的老系统却是 16 核,那么理论上我们可以对其进行权重配置值,即就是 32 核服务器的处理能力是 16 核的两倍,负载均衡算法权重比例调整为 2:1,让更多的请求分发给 32 核的服务器。
加权轮询解决了轮询算法中无法根据服务器的配置差异进行更好的任务分配的问题,其实还是会存在无法根据服务器的状态差异性进行请求任务分配的问题。
负载最低优先
负载系统将请求分配给当前负载最低的服务器,这里的负载根据不同请求类型和业务处理场景,可以用不同的指标来衡量。比如以下几个场景:
- LVS 这种 4 层网络负载均衡设备,可以以连接数来判断服务器的状态,服务器连接数量越大,表明服务器压力就越大。
- Nginx 这种 7 层网络负载均衡系统,可以以 HTTP 请求数量判断服务器的状态(Nginx 内置的负载均衡算法不支持这种方式,需要自行进行扩展)。
如果我们是自己研发负载均衡系统,可以根据业务特点来选择衡量系统压力的指标。如果 CPU 是密集型,可以以 CPU 负载来衡量系统的压力;如果是 IO 密集型,则可以以 IO 负载来衡量系统压力。
最少链接数优先算法解决了轮询算法中无法感知服务器状态的问题,但是由此带来的代价是复杂度增加很多。比如:最少链接数优先的算法要求负载系统统计每个服务器当前建立的链接,其应用场景仅限于负载均衡接收的任何请求都会转发给服务器进行处理,否则如果负载均衡系统和服务之间是固定的连接池方式,就不适合采取这种算法。LVS 可以采取这种算法进行负载均衡,而一个通过连接池的方式链接数据库 MySQL 集群的负载均衡系统就不适合采取这种算法进行负载均衡了。
CPU 负载均衡最低优先的算法要求负载均衡系统以某种方式收集每个服务器的 CPU 的具体负载情况,同时要确定是以一分钟的负载标准,还是以 10 分钟、15 分钟的负载标准,不存在 1 分钟肯定比 15 分钟的好或差。不同业务最优的时间间隔也是不一样的,时间间隔太短容易造成频繁波动,时间太长可能造成峰值来临时响应缓慢。
负载最低优先的算法基本上能够很完美地解决了轮询算法的缺点,也因为采用负载最低优先算法后,负载均衡系统需要感知服务器当前运行状态,此时同样造成代价上升很多。对于开发者来说也许轮询算法只要简短的代码就可以实现,然而负载最低优先算法需要大量的代码来实现。
负载最低优先看起来是解决了轮询中的缺点,但由于其复杂度的提升,导致真正使用中比例还不如轮训或者轮询加权算法。


