前文分析 DividePlugin
插件时发现 Soul 网关的负载均衡策略有三种,分别为 Random、Hash、RoundRobin。今天做一个初步研究。
准备工作
依次运行soul-admin
、soul-bootstrap
项目。
之后分别以 8188,8189,8190 启动三个soul-examples-http
项目。
在soul-admin
的配置中将三个项目的权重进行设置。
源码分析
负载均衡的类图比较清晰:
其中 LoadBalance
是 SPI
接口。AbstractLoadBalance
是抽象类,实现了模版方法select
,并定义了一个抽象方法doSelect
供各子类去实现。
Random
设置负载均衡策略为 random,在以下代码中打上断点。
public class DividePlugin extends AbstractSoulPlugin {
@Override
protected Mono<Void> doExecute(final ServerWebExchange exchange, final SoulPluginChain chain, final SelectorData selector, final RuleData rule) {
// ...
/** 从规则中设置的负载均衡算法来计算真实去转发的服务 */
DivideUpstream divideUpstream = LoadBalanceUtils.selector(upstreamList, ruleHandle.getLoadBalance(), ip);
// ...
}
}
前进到AbstractLoadBalance
的模版方法select
。
public DivideUpstream select(final List<DivideUpstream> upstreamList, final String ip) {
if (CollectionUtils.isEmpty(upstreamList)) {
return null;
}
if (upstreamList.size() == 1) {
return upstreamList.get(0);
}
return doSelect(upstreamList, ip);
}
如果可选服务信息为空则直接返回,若只有一个可选服务,则直接返回这一条即可。其余情况进入所选负载均衡规则的子类进行后续处理。
接着进入RandomLoadBalance
类的实现方法doSelect
逻辑。
public DivideUpstream doSelect(final List<DivideUpstream> upstreamList, final String ip) {
// 1. 根据总服务列表计算一个总的权重,第一次请求就是把三个项目权重相加,为401
int totalWeight = calculateTotalWeight(upstreamList);
// 2. 判断三个项目中的权重是否一样
boolean sameWeight = isAllUpStreamSameWeight(upstreamList);
// 3. 三个项目中的权重一样与否使用的是不同的 random 算法
if (totalWeight > 0 && !sameWeight) {
return random(totalWeight, upstreamList);
}
// If the weights are the same or the weights are 0 then random
return random(upstreamList);
}
// 4. 三个项目中的权重一样时使用的 random 算法
private DivideUpstream random(final List<DivideUpstream> upstreamList) {
// 5. 权重一样时,从服务列表里随机按照 index 去一个数据使用
return upstreamList.get(RANDOM.nextInt(upstreamList.size()));
}
// 6. 三个项目中的权重不一样时使用的 random 算法
private DivideUpstream random(final int totalWeight, final List<DivideUpstream> upstreamList) {
// 7. 根据总权重随机一个偏移量
int offset = RANDOM.nextInt(totalWeight);
// 8. 循环服务列表,用偏移量去减去各自权重,最终取第一个偏移量为负值的服务
for (DivideUpstream divideUpstream : upstreamList) {
offset -= getWeight(divideUpstream);
if (offset < 0) {
return divideUpstream;
}
}
return upstreamList.get(0);
}
先根据服务列表各自的权重生成一个总的权重。之后如果各个服务的权重是一样的,会随机在列表里选一个记录返回;若各个服务权重不一样,则会根据总权重随机一个偏移量,之后循环服务列表,每一次将偏移量和服务各自的权重相减。返回第一个使偏移量为负的服务。这种方式也能保证权重大的服务会更大概率被选中。