一、前戏
本篇是divide插件的三大负载均衡算法的最后一种的分析,也是相对前两种理解上稍微难一点。
RoundRobin(轮询)通过遍历自增每个请求连接的当前权重,选出最高权重连接被使用,同时通过与总权重的差值,来降低其当前权重,从而达到负载均衡的目的。
二、源码分析
// 以选择器中负载均衡list中第一条数据的ip为key,value存储整个负载均衡list
private final ConcurrentMap<String, ConcurrentMap<String, WeightedRoundRobin>> methodWeightMap = new ConcurrentHashMap<>(16);
// 通过原子类实现的cas锁(乐观锁)
private final AtomicBoolean updateLock = new AtomicBoolean();
public DivideUpstream doSelect(final List<DivideUpstream> upstreamList, final String ip) {
// 选择器中负载均衡list中第一条数据的ip为key,查询methodWeightMap是否存在
String key = upstreamList.get(0).getUpstreamUrl();
ConcurrentMap<String, WeightedRoundRobin> map = methodWeightMap.get(key);
if (map == null) {
// 查询methodWeightMap是不存在,创建16个容量的ConcurrentHashMap
methodWeightMap.putIfAbsent(key, new ConcurrentHashMap<>(16));
// map是这个新建ConcurrentHashMap的引用,后面代码操作map实际就是操作methodWeightMap
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);
// 遍历负载均衡list,将"不存在"请求连接添加到map中
if (weightedRoundRobin == null) {
weightedRoundRobin = new WeightedRoundRobin();
weightedRoundRobin.setWeight(weight);
map.putIfAbsent(rKey, weightedRoundRobin);
}
// 比较同一连接,权重发生更改,重新赋值
if (weight != weightedRoundRobin.getWeight()) {
//weight changed
weightedRoundRobin.setWeight(weight);
}
// 关键点1:缓存的节点权重增加"最新权重",并设置更新时间用于过期比较
long cur = weightedRoundRobin.increaseCurrent();
weightedRoundRobin.setLastUpdate(now);
// 关键点2:选出当前权重最大的连接对象
if (cur > maxCurrent) {
maxCurrent = cur;
selectedInvoker = upstream;
// 记录最大权重引用
selectedWRR = weightedRoundRobin;
}
// 计算总权重
totalWeight += weight;
}
// 关键点3:剔除过期的连接,通过原子类保证线程安全
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);
}
}
// 关键点4: 相当于服务降级,当前最大权重 - 总权重 = "当前最大权重"
if (selectedInvoker != null) {
// 通过引用操作原对象
selectedWRR.sel(totalWeight);
return selectedInvoker;
}
// should not happen here
return upstreamList.get(0);
}
此处模拟下三个节点,三次调用的计算过程

三、小结
- 疑问1: 如果修改负载均衡list的第一条,数据会产生冗余?

- 疑问2:关于源码分析中的关键点3,原子类实现cas锁,代码“看懂”,却没有模拟出场景来,难受!
- 日拱一卒
网友评论