序
本文主要研究一下 DependenciesBasedLoadBalancer
DependenciesBasedLoadBalancer
spring-cloud-zookeeper-discovery-2.1.2.RELEASE-sources.jar!/org/springframework/cloud/zookeeper/discovery/dependency/DependenciesBasedLoadBalancer.java
public class DependenciesBasedLoadBalancer extends DynamicServerListLoadBalancer {private static final Log log = LogFactory.getLog(DependenciesBasedLoadBalancer.class);
private final Map<String, IRule> ruleCache = new ConcurrentHashMap<>();
private final ZookeeperDependencies zookeeperDependencies;
public DependenciesBasedLoadBalancer(ZookeeperDependencies zookeeperDependencies,
ServerList<?> serverList, IClientConfig config, IPing iPing) {super(config);
this.zookeeperDependencies = zookeeperDependencies;
setServersList(serverList.getInitialListOfServers());
setPing(iPing);
setServerListImpl(serverList);
}
@Override
public Server chooseServer(Object key) {
String keyAsString;
if ("default".equals(key)) { // this is the default hint, use name instead
keyAsString = getName();}
else {keyAsString = (String) key;
}
ZookeeperDependency dependency = this.zookeeperDependencies
.getDependencyForAlias(keyAsString);
log.debug(String.format("Current dependencies are [%s]",
this.zookeeperDependencies));
if (dependency == null) {
log.debug(String.format("No dependency found for alias [%s] - will use the default rule which is [%s]",
keyAsString, this.rule));
return this.rule.choose(key);
}
cacheEntryIfMissing(keyAsString, dependency);
log.debug(String.format("Will try to retrieve dependency for key [%s]. Current cache contents [%s]",
keyAsString, this.ruleCache));
updateListOfServers();
return this.ruleCache.get(keyAsString).choose(key);
}
private void cacheEntryIfMissing(String keyAsString, ZookeeperDependency dependency) {if (!this.ruleCache.containsKey(keyAsString)) {log.debug(String.format("Cache doesn't contain entry for [%s]", keyAsString));
this.ruleCache.put(keyAsString,
chooseRuleForLoadBalancerType(dependency.getLoadBalancerType()));
}
}
private IRule chooseRuleForLoadBalancerType(LoadBalancerType type) {switch (type) {
case ROUND_ROBIN:
return getRoundRobinRule();
case RANDOM:
return getRandomRule();
case STICKY:
return getStickyRule();
default:
throw new IllegalArgumentException("Unknown load balancer type" + type);
}
}
private RoundRobinRule getRoundRobinRule() {return new RoundRobinRule(this);
}
private IRule getRandomRule() {RandomRule randomRule = new RandomRule();
randomRule.setLoadBalancer(this);
return randomRule;
}
private IRule getStickyRule() {StickyRule stickyRule = new StickyRule(getRoundRobinRule());
stickyRule.setLoadBalancer(this);
return stickyRule;
}
}
- DependenciesBasedLoadBalancer 继承了 com.netflix.loadbalancer.DynamicServerListLoadBalancer
- 其 chooseServer 方法会使用 zookeeperDependencies.getDependencyForAlias 来跟进 key 获取 ZookeeperDependency,如果 dependency 为 null 则直接使用 rule.choose(key),不为 null 则进行 chahe,然后更新 server 列表,最后通过 ruleCache.get(keyAsString).choose(key) 返回
- cacheEntryIfMissing 方法会根据 ZookeeperDependency.getLoadBalancerType() 进行 chooseRuleForLoadBalancerType,这里分为了 ROUND_ROBIN、RANDOM、STICKY 三种
StickyRule
spring-cloud-zookeeper-discovery-2.1.2.RELEASE-sources.jar!/org/springframework/cloud/zookeeper/discovery/dependency/StickyRule.java
public class StickyRule extends AbstractLoadBalancerRule {private static final Log log = LogFactory.getLog(StickyRule.class);
private final IRule masterStrategy;
private final AtomicReference<Server> ourInstance = new AtomicReference<>(null);
private final AtomicInteger instanceNumber = new AtomicInteger(-1);
public StickyRule(IRule masterStrategy) {this.masterStrategy = masterStrategy;}
@Override
public void initWithNiwsConfig(IClientConfig iClientConfig) { }
@Override
public Server choose(Object key) {final List<Server> instances = getLoadBalancer().getServerList(true);
log.debug(String.format("Instances taken from load balancer [%s]", instances));
Server localOurInstance = this.ourInstance.get();
log.debug(String.format("Current saved instance [%s]", localOurInstance));
if (!instances.contains(localOurInstance)) {this.ourInstance.compareAndSet(localOurInstance, null);
}
if (this.ourInstance.get() == null) {Server instance = this.masterStrategy.choose(key);
if (this.ourInstance.compareAndSet(null, instance)) {this.instanceNumber.incrementAndGet();
}
}
return this.ourInstance.get();}
/**
* Each time a new instance is picked, an internal counter is incremented. This way
* you can track when/if the instance changes. The instance can change when the
* selected instance is not in the current list of instances returned by the instance
* provider
* @return instance number
*/
public int getInstanceNumber() {return this.instanceNumber.get();
}
}
- StickyRule 继承了 com.netflix.loadbalancer.AbstractLoadBalancerRule;其 choose 方法首先通过 getLoadBalancer().getServerList(true) 获取 server 列表,对于该列表没有 localOurInstance 的,则更新本地引用为 null;然后判断 localOurInstance 是否为 null,为 null 的话则使用 masterStrategy.choose(key) 进行选择然后更新;最后返回 ourInstance.get()
小结
- DependenciesBasedLoadBalancer 继承了 com.netflix.loadbalancer.DynamicServerListLoadBalancer
- 其 chooseServer 方法会使用 zookeeperDependencies.getDependencyForAlias 来跟进 key 获取 ZookeeperDependency,如果 dependency 为 null 则直接使用 rule.choose(key),不为 null 则进行 chahe,然后更新 server 列表,最后通过 ruleCache.get(keyAsString).choose(key) 返回
- cacheEntryIfMissing 方法会根据 ZookeeperDependency.getLoadBalancerType() 进行 chooseRuleForLoadBalancerType,这里分为了 ROUND_ROBIN、RANDOM、STICKY 三种
doc
- DependenciesBasedLoadBalancer