githubEdit

2. LeastResourceUsageWithWeight

2.1 候选broker池

LeastResourceUsageWithWeight使用跟ThresholdShedder同样的打分算法对broker进行打分,即使用加权最大资源使用率,结合历史分数来加权计算得到当前分数,并得到所有broker的平均分。最后根据配置阈值loadBalancerAverageResourceUsageDifferenceThresholdPercentage来挑选候选broker。

final double avgUsage = totalUsage / candidates.size();
final double diffThreshold =
        conf.getLoadBalancerAverageResourceUsageDifferenceThresholdPercentage() / 100.0;
candidates.forEach(broker -> {
    Double avgResUsage = brokerAvgResourceUsageWithWeight.getOrDefault(broker, MAX_RESOURCE_USAGE);
    if ((avgResUsage + diffThreshold <= avgUsage)) {
        bestBrokers.add(broker);
    }
});
# The broker average resource usage difference threshold.
# Average resource usage difference threshold to determine a broker whether to be a best candidate in LeastResourceUsageWithWeight.
# (eg: broker1 with 10% resource usage with weight and broker2 with 30% and broker3 with 80% will have 40% average resource usage.
# The placement strategy can select broker1 and broker2 as best candidates.)
# It only takes effect in the LeastResourceUsageWithWeight strategy.
loadBalancerAverageResourceUsageDifferenceThresholdPercentage=10

如果某个broker的打分加上此阈值仍然不大于平均分数,则该broker会加入到候选broker列表里,得到候选broker列表后,从中随机挑选一个broker;如果没有候选broker,则从全体broker里随机挑选。

例如,当 broker1 的资源使用率为 10%、broker2 的资源使用率为 30%、broker3 的资源使用率为 80% 时,经计算可得平均资源使用率为 40%。在这种情况下,放置策略可将 Broker1 和 Broker2 选定为最佳候选者。原因在于设定的阈值为 10,且 10 + 10 ≤ 40,30 + 10 ≤ 40 ,满足筛选条件。如此一来,从 broker3 卸载下来的 bundle 将能够较为均匀地分摊到 broker1 与 broker2 上,而非全部集中到最低载的 broker1。

回顾前文所介绍的过度加载问题,由于此处是依据资源使用率对 broker 进行打分排序,无法将刚分配的 bundle 的负载估算至 broker 上,所以若仅选择打分最低的 broker,便会引发过度加载问题。为此,LeastResourceUsageWithWeight 引入了新配置 loadBalancerAverageResourceUsageDifferenceThresholdPercentage,先挑选出一批候选 broker,然后再随机选择,以此避免过度加载问题。

然而在实践过程中会发现,loadBalancerAverageResourceUsageDifferenceThresholdPercentage 这一参数很难确定一个恰当的值,以至于频繁触发作为兜底策略的全局随机挑选逻辑。

比如说,当前集群有6个broker,打分分别为40,40,40,40,69,70,则平均分为49.83,使用默认的配置,则没有候选broker,因为40+10>49.83,此时触发兜底的全局随机挑选逻辑,因此bundle可能会从高载的broker5卸载到同样高载的broker6,或者反过来,而此类错误的负载均衡决策恰恰是集群不断执行负载均衡操作的根本原因。与之不同的是,优秀的负载均衡算法通常仅需一两次决策便可达成理想效果。

若尝试调小配置值以扩大随机池,例如将其设为 0,那么部分处于高负载状态的 broker 也可能会被纳入候选 broker 列表。如下例,当前集群有5个broker,打分分别为10,60,70,80,80,则平均分为60,配置值为0,则broker1、broker2都是候选broker,此时如果broker2分担一半卸载下来的流量,它大概率会超载。

因此LeastResourceUsageWithWeight算法无法避免错误的负载均衡(过度加载问题)。当然,如果你要使用ThresholdShedder算法的话,ThresholdShedder + LeastResourceUsageWithWeight这个组合还是会优胜于 ThresholdShedder + LeastLongTermMessageRate 组合,因为起码LeastResourceUsageWithWeight的打分算法跟ThresholdShedder 的是一致的。

2.2 历史权值算法

最后,我们再介绍一下历史权值算法引起的问题。历史权值算法被ThresholdShedder、LeastResourceUsageWithWeight算法所使用,算法如下:

historyUsage = historyUsage == null ? resourceUsage : historyUsage * historyPercentage + (1 - historyPercentage) * resourceUsage;

historyPercentage默认值为0.9,可见上一次计算得到的分数对当前分数占据了绝大部分影响,当前的最大资源使用率只占0.1,短期的剧烈负载波动不会立马反映到负载分数上,从而解决了负载抖动的问题,但是引入这个算法是有巨大副作用的。

比如过度卸载的问题。如下例,当前集群有1台broker1,负载为90%,由于其稳定运行了,因此历史打分为90,现加入一台broker2,其当前负载为10%。则

  • 第一次执行shedding:broker1打分90,broker2打分10,为简单起见假设算法会移动部分bundles以使得两者的score相同,则负载迁移完成后broker1、broker2的真实负载均为50分。

  • 第二次执行shedding:broker1打分90*0.9+50*0.1=86,broker2打分10*0.9+50*0.1=14。

    • 注意这里broker1的真实负载是50,但是负载得分却高估为86!broker2的真实负载也是50,但是却低估为14!

    • 虽然两者的真实负载已经相同,但是由于两者打分仍然悬殊,还会继续卸载流量。因此,从broker1中卸载36分对应的流量给broker2,导致broker1的负载变成14,broker2的负载变成86,负载倒过来了!

  • 第三次执行shedding:broker1打分86*0.9+14*0.1=78.8,broker2打分14*0.9+86*0.1=21.2,这个时候还是判断broker1负载比broker2高,还是会从broker1卸载流量给broker2,那么会把broker1身上剩下的所有负载都卸了,broker1变成跟broker2刚加入一样,不承担任何bundles,所有负载加到broker2上了。

这个例子虽然是一个理想化的理论分析,但是我们还是可以看到,采用历史打分算法会严重错估broker的真实负载,特别是在broker新增负载或者降低负载的时候,它虽然能避免负载抖动的问题,但是会引入更严重且更广泛的问题:错估broker的真实负载,从而导致错误的负载均衡执行。在这个例子中,就导致了过度卸载的问题。

如果用户想避免负载错估的缺陷,可以把配置loadBalancerHistoryResourcePercentage设置为0,但是这就无法应对负载抖动的问题了。此所谓鱼与熊掌难以兼得,而这恰恰是我开发全新算法 AvgShedder 的关键缘由,该算法能够达成鱼与熊掌兼而有之的效果。

2.3 算法组合

LeastResourceUsageWithWeight 是专门为配合 ThresholdShedder 而设计并实现的。所以,若要使用 LeastResourceUsageWithWeight,就必须与 ThresholdShedder 协同使用。

Last updated

Was this helpful?