soul的divide插件负载均衡算法之权重轮询算法
众所周知,轮询算法是每一次请求访问的服务器是在上一次请求访问的服务器的下一个位置。
而soul网关提供了带有权重的轮询。
public class RoundRobinLoadBalance extends AbstractLoadBalance {
private final int recyclePeriod = 60000;
private final ConcurrentMap<String, ConcurrentMap<String, WeightedRoundRobin>> methodWeightMap = new ConcurrentHashMap<>(16);
private final AtomicBoolean updateLock = new AtomicBoolean();
@Override
public DivideUpstream doSelect(final List<DivideUpstream> upstreamList, final String ip) {
String key = upstreamList.get(0).getUpstreamUrl();
ConcurrentMap<String, WeightedRoundRobin> map = methodWeightMap.get(key);
if (map == null) {
methodWeightMap.putIfAbsent(key, new ConcurrentHashMap<>(16));
map = methodWeightMap.get(key);
}
int totalWeight = 0;
long maxCurrent = Long.MIN_VALUE;
long now = System.currentTimeMillis();
DivideUpstream selectedInvoker = null;
WeightedRoundRobin selectedWRR = null;
for (DivideUpstream upstream : upstreamList) {
String rKey = upstream.getUpstreamUrl();
WeightedRoundRobin weightedRoundRobin = map.get(rKey);
int weight = getWeight(upstream);
if (weightedRoundRobin == null) {
weightedRoundRobin = new WeightedRoundRobin();
weightedRoundRobin.setWeight(weight);
map.putIfAbsent(rKey, weightedRoundRobin);
}
if (weight != weightedRoundRobin.getWeight()) {
//weight changed
weightedRoundRobin.setWeight(weight);
}
long cur = weightedRoundRobin.increaseCurrent();
weightedRoundRobin.setLastUpdate(now);
if (cur > maxCurrent) {
maxCurrent = cur;
selectedInvoker = upstream;
selectedWRR = weightedRoundRobin;
}
totalWeight += weight;
}
if (!updateLock.get() && upstreamList.size() != map.size() && updateLock.compareAndSet(false, true)) {
try {
// copy -> modify -> update reference
ConcurrentMap<String, WeightedRoundRobin> newMap = new ConcurrentHashMap<>(map);
newMap.entrySet().removeIf(item -> now - item.getValue().getLastUpdate() > recyclePeriod);
methodWeightMap.put(key, newMap);
} finally {
updateLock.set(false);
}
}
if (selectedInvoker != null) {
selectedWRR.sel(totalWeight);
return selectedInvoker;
}
// should not happen here
return upstreamList.get(0);
}
/**
* The type Weighted round robin.
*/
protected static class WeightedRoundRobin {
private int weight;
private final AtomicLong current = new AtomicLong(0);
private long lastUpdate;
/**
* Gets weight.
*
* @return the weight
*/
int getWeight() {
return weight;
}
/**
* Sets weight.
*
* @param weight the weight
*/
void setWeight(final int weight) {
this.weight = weight;
current.set(0);
}
/**
* Increase current long.
*
* @return the long
*/
long increaseCurrent() {
return current.addAndGet(weight);
}
/**
* Sel.
*
* @param total the total
*/
void sel(final int total) {
current.addAndGet(-1 * total);
}
/**
* Gets last update.
*
* @return the last update
*/
long getLastUpdate() {
return lastUpdate;
}
/**
* Sets last update.
*
* @param lastUpdate the last update
*/
void setLastUpdate(final long lastUpdate) {
this.lastUpdate = lastUpdate;
}
}
}
methodWeightMap 这一成员变量保存上一次访问的状态。外部map的key是访问的ip, 内部map的key是访问的url,value是访问的状态。
对于有权重的算法,其调用流程如下:
1. 遍历访问列表,对每一个服务的状态值加上权重并进行更新,得到新的状态值。
2. 从这些状态值中选出当前最大的状态值,其对应的服务就是本次访问的服务
3. 将选中服务的状态值减去总权重并进行更新
这样,便可以保证权重较大的服务被访问的次数更多。
下面,是三台权重分别为 5、15、20 的三台服务器采用soul的RoundRobin算法的调用流程:
在以上三次请求中,权重为20的服务被调用了两次,而权重为5的服务一次也没有调用,看来权重还是产生其对应的效果😄