聊聊DependenciesBasedLoadBalancer

240 阅读2分钟

本文主要研究一下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