平滑加权轮询(Smooth Weighted Round Robin)
平滑加权轮询(Smooth Weighted Round Robin,简称 SWRR)是一种负载均衡算法,它结合了 轮询(Round Robin) 和 加权负载均衡 的思想,并通过平滑的权重调整来确保请求分配的均衡性。SWRR 的核心在于根据各个服务器的权重动态地调整请求的分配,使得负载均衡更为平稳和有效。
传统轮询算法
轮询算法(Round Robin)是最简单的负载均衡算法,它的基本思想是按顺序将请求轮流分配到不同的服务器上。这个方法适合于每个服务器性能大致相同的情况,但当服务器的性能差异较大时,轮询算法就不再适用了,因为它没有考虑服务器的负载能力或性能差异。
问题: 传统的轮询算法无法有效处理服务器性能不均衡的情况。比如,如果有一台服务器的性能较差,它将会接收到与其他服务器相同数量的请求,造成性能瓶颈,而其他性能更好的服务器会相对闲置。
加权轮询(Weighted Round Robin)
加权轮询是对传统轮询算法的一种扩展,它为每台服务器分配一个权重值,然后根据权重的比例来分配请求。具有更高权重的服务器会接收到更多的请求,而权重较低的服务器则接收较少的请求。
问题: 在加权轮询中,服务器的请求分配是基于预设的权重值,且每一轮加权周期内,服务器接收的请求是完全固定的。这意味着即使某台服务器的负载增加,它仍然会按照设定的权重接收相同数量的请求,从而可能导致某些服务器负载不均衡。
平滑加权轮询(SWRR)的原理
平滑加权轮询(SWRR)结合了加权轮询的优点,同时改进了权重的动态调节,从而避免了负载的过度集中。它通过以下几个步骤来实现更加平滑的负载分配:
权重计算
每台服务器都有一个权重(Weight),该权重表示服务器的处理能力或重要性,权重值越大,服务器处理请求的能力就越强。在 SWRR 中,服务器的请求分配会根据权重动态调整。
动态调整权重
在传统的加权轮询中,权重是固定的,每个周期内每台服务器会根据其权重接收固定数量的请求。而在平滑加权轮询中,每轮分配请求时,服务器的当前权重(CurrentWeight)会动态变化。
- 每台服务器的
CurrentWeight
会根据其Weight
值累加。 - 每次请求分配时,选择
CurrentWeight
最大的服务器进行请求处理。 - 被选中的服务器的
CurrentWeight
会减去所有服务器的总权重,确保下一轮能够公平地分配请求。
平滑的请求分配
通过平滑地调整服务器的当前权重,SWRR 可以在多个服务器之间实现较为均衡的请求分配,即使服务器的处理能力有所差异。具体而言,平滑加权轮询的过程可以如下描述:
- 初始时,每个服务器的
CurrentWeight
为 0,权重值由其Weight
决定。 - 在每个轮次,所有服务器的
CurrentWeight
都会累加其Weight
,并从中选择权重最大的服务器进行处理。 - 被选中的服务器的
CurrentWeight
会减去所有服务器的总权重,从而确保服务器间的负载均衡。
通过这样的处理,SWRR 不仅能考虑服务器的权重,还能平滑地调整权重,从而避免某些服务器接收过多的请求,造成过载。
算法原理
假设有三台服务器,分别是 A、B 和 C,它们的权重分别是 5、1 和 1。平滑加权轮询算法的基本步骤描述:
-
初始化服务器:
- A: Weight = 5, CurrentWeight = 0
- B: Weight = 1, CurrentWeight = 0
- C: Weight = 1, CurrentWeight = 0
-
开始请求分配:
-
计算总权重 = 5 + 1 + 1 = 7
-
所有服务器的 CurrentWeight 都会增加它们的 Weight
- A: CurrentWeight = 0 + 5 = 5
- B: CurrentWeight = 0 + 1 = 1
- C: CurrentWeight = 0 + 1 = 1
-
选择
CurrentWeight
最大的服务器:A -
选中服务器 A 后,CurrentWeight 会减去总权重。
- A: CurrentWeight = 5 - 7 = -2
-
-
下一轮分配:
-
计算总权重 = 7(不变)
-
所有服务器的 CurrentWeight 再次增加它们的 Weight
- A: CurrentWeight = -2 + 5 = 3
- B: CurrentWeight = 1 + 1 = 2
- C: CurrentWeight = 1 + 1 = 2
-
选择
CurrentWeight
最大的服务器:A -
选中服务器 A 后, CurrentWeight 会减去总权重。
- A: CurrentWeight = 3 - 7 = -4
-
-
继续分配,直到请求结束:
- 通过这种方式,服务器的请求分配是动态的,并且每台服务器的负载在一段时间内趋于均衡
第 i 次选择 | weightSum += weight | CurrentWeight = max(weight) | max(weight) - totalWeight |
---|---|---|---|
1 | { 5 , 1 , 1 } | 5(A) | { -2 , 1 , 1 } |
2 | { 3 , 2 , 2 } | 3(A) | { -4 , 2 , 2 } |
3 | { 1 , 3 , 3 } | 3(B) | { 1 , -4 , 3 } |
4 | { 6 , -3 , 4 } | 6(A) | { -1 , -3 , 4 } |
5 | { 4 , -2 , 5 } | 5© | { 4 , -2 , -2 } |
6 | { 9 , -1 , -1 } | 9(A) | { 2 , -1 , -1 } |
7 | { 7 , 0 , 0 } | 7(A) | { 0 , 0 , 0 } |
经过七轮之后又回到了初始权重值0,0,0;各个节点的负载均衡情况是 AABACAA,可以看到 A 节点没有连续作为工作节点,而是穿插在BC两个节点中进行,这样不会一直处理进来的请求,减小服务器的负载压力。
用一句形象的话描述:就像割韭菜,长到一定的高度就割韭菜,权限重的就长得快,割的次数就多,但是长得快的割得狠,所以下次就割别的韭菜了。