分布式 | 常见的负载均衡算法

2021-03-19 14:40:59 浏览数 (1)

常见负载均衡算法

为了提高项目整体的并发和可用性,我们往往会对同一个项目部署多个实例,这时就需要根据不同的算法来进行负载均衡,下面来介绍一下常见的负载均衡算法

静态负载均衡算法包括:随机、轮询、比率、优先权。

动态负载均衡算法包括:最少连接数、最快响应速度、观察方法、预测法、动态性能分配、动态服务器补充、服务质量、服务类型、规则模式。

1. 随机

这种算法相对简单,先获取可用服务实例的总数量,然后从随机取一个,如果数量小的情况下,可能一直随机到一台服务实例上

代码语言:javascript复制
List<Server> upList = lb.getReachableServers();

int serverCount = upList.size();
if (serverCount == 0) {
  return null;
}

int index = chooseRandomInt(serverCount);
server = upList.get(index);

2. 轮询

负载均衡系统接收到请求后,按照顺序轮流分配给服务器。

这种方式非常简单,只管按顺序分配,至于服务器当前负载情况、硬件能力等都不关心,只要服务器还能工作,就可以分配,除非服务器挂了。

这种方案需要记录请求的次数,然后对服务数取模即可得到对应的示例次序

代码语言:javascript复制
private AtomicInteger nextServerCyclicCounter = new AtomicInteger(0);

public Server choose() {
  Server server = null;
  List<Server> reachableServers = lb.getReachableServers();
  int upCount = reachableServers.size();

  int nextServerIndex = incrementAndGetModulo(upCount);
  server = reachableServers.get(nextServerIndex);
  return server;
}

private int incrementAndGetModulo(int modulo) {
  for (;;) {
    int current = nextServerCyclicCounter.get();
    int next = (current   1) % modulo;
    if (nextServerCyclicCounter.compareAndSet(current, next))
      return next;
  }
}

3. 加权轮询

是轮询方式的一种改进,轮询方式是无差别分配,但实际服务器的处理能力是有差异的,所以需要区别对待。

为服务器设置权值,权值高的就多分配点。这种方式相当于人为的去指定那个服务处理更快一点

例如我们有三个服务 a:4, b:3, c:1, 在这样的权重中,可以保证轮询算法结果为 a、a、a、a、b、b、b、c 然后往复即可,这种方式实现简单,但会导致权重的大的服务一直会处理请求造成短时流量增长,而权重低的会一直空闲着

下面我们来看看 Nginx 是如何处理加权轮询的

原文来自: https://blog.csdn.net/aiqu9621/article/details/101624287

nginx可以指定轮询几率,weight和访问比率成正比,用于后端服务器性能不均的情况。 例如:

代码语言:javascript复制
upstream backend {
  server a weight=6;
  server b weight=3;
  server c weight=1;
}

按照配置,每有10次请求,其中6个会转发到a服务器,3个转发到b服务器,1个转发到c服务器。

每个服务器都有三个权重变量,先解释下它们的含义。

(1) weight

配置文件中指定的该服务器的权重,这个值是固定不变的。

(2) effective_weight

服务器的有效权重,初始值为weight。

在释放服务器时,如果发现和某服务器的通信过程中发生了错误,就减小它的effective_weight。 此后有新的请求过来时,在选取该服务器的过程中,再逐步增加effective_weight,最终又恢复到weight。 之所以增加这个字段,是为了当服务器发生错误时,降低其权重。

(3) current_weight

服务器目前的权重,初始为0,之后会动态调整。

那么如何动态调整呢?

nginx每次选取服务器时:

  1. 先遍历集群中所有服务器,将每个服务器的current_weight增加它的effective_weight,
  2. 再累加所有服务器的effective_weight,保存为total。
  3. 判断当前服务器的current_weight是否最大,是则选中该服务器,然后把它的current_weight减去total。不是则不会被选中,current_weight也就不用减了。

弄清了三个weight字段的含义后,加权轮询算法可描述为:

  1. 对于每个请求,遍历集群中的所有可用服务器,对于每个服务器执行:current_weight = effecitve_weight。
  2. 累加所有effective_weight,保存为total。
  3. 选出current_weight最大的服务器,作为本次选定的服务器。
  4. 对于本次选定的服务器,执行:current_weight -= total。

下面以表格形式记录其过程:

请求次数

开始current_weight

增加effective_weight

累加total

选中服务器

选中后current_weight

1

[0, 0, 0]

[6, 3, 1]

10

a

[-4, 3, 1]

2

[-4, 3, 1]

[2, 6, 2]

10

b

[2, -4, 2]

3

[2, -4, 2]

[8, -1, 3]

10

a

[-2, -1, 3]

4

[-2, -1, 3]

[4, 2, 4]

10

a

[-6, 2, 4]

5

[-6, 2, 4]

[0, 5, 5]

10

b

[0, -5, 5]

6

[0, -5, 5]

[6, -2, 6]

10

a

[-4, -2, 6]

7

[-4, -2, 6]

[2, 1, 7]

10

c

[2, 1, -3]

8

[2, 1, -3]

[8, 4, -2]

10

a

[-2, 4, -2]

9

[-2, 4, -2]

[4, 7, -1]

10

b

[4, -3, -1]

10

[4, -3, -1]

[10, 0, 0]

10

a

[0, 0, 0]

可以看到,选中服务器依次为[a, b, a, a, b, a, c, a, b, a]。

a,b,c分别被选中了6,3,1次,正好是符合其权重值的; 服务器a虽然权重大,但没有被连续选取,不会对a服务器连续请求; 经过10次请求后,a,b,c的当前权重current_weight又全部归0,如此便可循环往复。

ps: 这里我们发现total永远都是10,因为这里假定服务器都没有发生故障或返回错误,其effective_weight不变。实际中如果服务器发生了错误,nginx当然也会进行降权处理,total也会变啦。这里我们学习一下正常算法,出错的情况就先不展开了。

4. 随机加权轮询

这种方式并不保证在一次完整的轮询中保证各个服务都能被轮询到,而是采用概率的方式来简化算法

如上面的示例 a:4, b:3, c:1,则总权重为 8,其中 a 占 4/8,b 占 3/8,c 占 1/8,然后生成一个随机数对 8 取模,然后就行范围对比即可得到对应的服务,

这种算法比 Nginx 的加权轮询简单的多

5. Hash

对请求中的关键信息(如IP)进行hash计算,hash值相同的请求分配到同一台服务器,例如业务中希望同一用户的请求都由同一台服务器来处理。

其中包括了 IP、URL 等都可以进行 hash 处理

6. P2C 算法

Power of Two Choices (P2C,两次随机选择) 负载均衡算法,主要用于为每个 RPC 请求返回一个 Server 节点以供调用,该算法策略出自论文 《The Power of Two Random Choices: A Survey of Techniques and Results》 ,主要思路如下:

  1. 从可用后端节点列表中做 2 次选择操作(随机算法 or 依据一定策略来选择),得到节点 nodeAnodeB
  2. 比较 nodeAnodeB 两个节点,选出负载最低(一般是正在处理的连接数 / 请求数最少)的节点作为被选中的节点

7. 其他动态算法

  • 最少连接数
  • 最快响应速度
  • 观察方法
  • 预测法
  • 动态性能分配
  • 动态服务器补充
  • 服务质量
  • 服务类型
  • 规则模式。

这类算法都需要在负载的执行过程中记录对应的服务的一些监控指标来选择最合适的服务实例。

优点: 这类算法可以动态的根据服务状态来进行负载,其灵活性更好,更能达到最优负载

缺点: 因为要监控服务的一些状态信息,其算法复杂度大大提高,同时还要收集服务器信息

参考

  1. Nginx 算法参考 https://blog.csdn.net/aiqu9621/article/details/101624287
  2. Power of Two Choices (P2C) https://linkerd.io/1/features/load-balancing/

0 人点赞