SpringCloud客户端负载均衡——Ribbon的LoadBalancer(负载均衡器)

所谓负载均衡,就是为资源分配负载,就是选择合适的服务处理请求。
SpringCloud的客户端负载均衡器LoadBalancerClient的默认实现RibbonLoadBalancerClient中,使用了RibbonILoadBalancer接口来实现chooseServer即负载均衡功能。

ILoadBalancer

// ILoadBalancer接口定义了负载均衡器的操作,包括初始化服务列表、选择服务实例、关停服务、获取服务列表等
interface ILoadBalancer {
	void addServers(List<Server> newServers);
	Server chooseServer(Object key);
	void markServerDown(Server server);
	List<Server> getReachableServers();
	List<Server> getAllServers();
}

ILoadBalancer的直接实现类是抽象类AbstractLoadBalancer

AbstractLoadBalancer

该抽象实现类,根据服务实例的状态,定义了一个服务分组枚举类ServerGroup,包含三种状态:All,Up,Not_Up
相应的定义了根据分组类型获取服务实例列表的getServerList方法
此外,还定义了获取负载均衡器统计信息的getLoadBalancerStats方法和选择服务实例的默认方法chooseServer

abstract class AbstractLoadBalancer implements ILoadBalancer{
	public enum ServerGroup {
		ALL, STATUS_UP, STATUS_NOT_UP
	}
	public Server chooseServer() { return chooseServer(null); } 
	public abstract List<Server> getServerList(ServerGroup serverGroup);
	public abstract LoadBalancerStats getLoadBalancerStats();
}

NoOpLoadBalancer——没有操作的实现类

BaseLoadBalancer

BaseLoadBalancer继承自AbstractLoadBalancer,实现了ILoadBalancer定义的所有方法和AbstractLoadBalancer中的抽象方法
BaseLoadBalancer是一个完整的负载均衡器的实现类,主要由以下职责:初始化服务实例列表、选择服务实例、关停服务、获取服务实例列表,

class BaseLoadBalancer extends AbstractLoadBalancer {
	psf IRule DEFAULT_RULE = new RoundRobinRule();
	psf SerialPingStrategy DEFAULT_PING_STRATEGY = new SerialPingStrategy();
	IRule rule = DEFAULT_RULE; // IRule接口有一个Server choose(Object key)方法,用于选择一个合适的服务实例,BaseLoadBalancer的chooseServer方法就是将工作委托给IRule的choose来完成,默认使用RoundRobinRule——线性负载均衡
	IPingStrategy pingStrategy = DEFAULT_PING_STRATEGY; // IPingStrategy接口有一个boolean[] pingServers(IPing, Server[]),定义了ping服务实例的策略,默认使用SerialPingStrategy——使用for循环线性遍历
	IPing ping = null; // IPing接口有一个boolean isAlive(Server)方法,用来定义如何去ping一个服务实例,判断其是否处于正常状态
	@Monitor(name = PREFIX + "AllServerList", type = DataSourceType.INFORMATIONAL)
	volatile List<Server> allServerList = Collections.synchronizedList(new ArrayList<>()); // 全部服务实例
	@Monitor(name = PREFIX + "UpServerList", type = DataSourceType.INFORMATIONAL)
	volatile List<Server> upServerList = Collections.synchronizedList(new ArrayList<>()); // UP正常状态的服务实例
	LoadBalancerStats lbStats; // 存储统计信息
	List<ServerListChangeListener> changeListeners = new CopyOnWriteArrayList<>(); // 监听服务变化的监听器,
	List<ServerStatusChangeListener> serverStatusListeners = new CopyOnWriteArrayList<>(); // 监听服务实例的状态变化的监听器,markServerDown时回调
	
	// -----------AbstractLoadBalancer接口定义的方法-----------
	void addServers(List<Server> newServers) { 
		ArrayList<Server> newList = new ArrayList<>();
		newList.addAll(allServerList); // 将原已维护的服务实例allServerList
		newList.addAll(newServers);    // 和新传入的服务实例newServers一起,加入到newList中
		setServersList(newList);	   // 然后调用setServersList更新服务实例清单
	}
	Server chooseServer(Object key) { 
		return rule.choose(key);	// 委托IRule选择服务实例
	}
	void markServerDown(Server server) { 
		server.setAlive(false);		// 标记服务状态
		notifyServerStatusChangeListener(Collections.singleton(server));	// 通知serverStatusListeners中的ServerStatusChangeListener,回调serverStatusChanged方法
	}
	List<Server> getReachableServers() { 
		return Collections.unmodifiableList(upServerList);	// 返回维护的正常服务实例清单upServerList
	}
	List<Server> getAllServers() {
		return Collections.unmodifiableList(allServerList); // 返回维护的所有服务实例清单allServerList
	}

	// -----------AbstractLoadBalancer定义的方法-----------
	List<Server> getServerList(ServerGroup serverGroup) { // 根据传入的不同分组,返回不同的服务实例清单
		ALL -> allServerList;
		STATUS_UP -> upServerList;
		STATUS_NOT_UP -> allServerList.removeAll(upServerList);
	}
	LoadBalancerStats getLoadBalancerStats() { // 返回统计信息
		return lbStats;
	}
	
	// -----------BaseLoadBalancer自身的方法-----------
	void setupPingTask() { // 启动ping任务,间隔10s检查allServerList中的Server是否健康,并将状态变化的server通知到serverStatusListeners
		// 由BaseLoadBalancer的各构造函数调用,以及ping相关设置变化时调用
		lbTimer = new ShutdownEnabledTimer(name="NFLoadBalancer-PingTimer-" + name, daemon=true);
		lbTimer.schedule(new PingTask(), delay=0, period=pingIntervalSeconds * 1000);
		forceQuickPing();
	}
	public void setServersList(List lsrv) {
		// 比较传入的lsrv中的Server和allServerList中的Server是否相同
		// 如果不同,对changeListeners中的每个ServerListChangeListener,回调其serverListChanged方法
		// 用lsrv中的Server覆盖allServerList
	}
}

DynamicServerListLoadBalancer

DynamicServerListLoadBalancer继承自BaseLoadBalancer,实现了服务实例清单的运行时动态更新能力、服务实例清单根据过滤器Filter的过滤功能。

class DynamicServerListLoadBalancer<T extends Server> extends BaseLoadBalancer {
	protected AtomicBoolean serverListUpdateInProgress = new AtomicBoolean(false);
	volatile ServerList<T> serverListImpl;	// 服务清单
	volatile ServerListFilter<T> filter;	// 过滤器
	protected final ServerListUpdater.UpdateAction updateAction = () -> { updateListOfServers(); };
	protected volatile ServerListUpdater serverListUpdater;	更新器

	public void updateListOfServers() {	// 被ServerListUpdater.UpdateAction接口的唯一方法doUpdate调用
		List<T> servers = serverListImpl.getUpdatedListOfServers(); // 如果serverListImpl不为null,则调用其getUpdatedListOfServers方法获取服务实例清单
		if (filter != null) {
			servers = filter.getFilteredListOfServers(servers);	// 如果filter不为null,则调用其getFilteredListOfServers方法对服务实例清单进行过滤 
		}
		updateAllServerList(servers);	// 更新服务实例清单
	}
	protected void updateAllServerList(List<T> ls) {
		for (T s : ls) {
			s.setAlive(true); // 将server设为alive=true,使得服务实例不必等到ping的时候才更新状态
		}
		setServersList(ls);	// 更新服务实例清单
		super.forceQuickPing();
	}
	@Override
	public void shutdown() {
		super.shutdown();
		stopServerListRefreshing();
	}
	public void stopServerListRefreshing() { serverListUpdater.stop(); } // 
	@Override
	public void setServersList(List lsrv) {
		super.setServersList(lsrv);
		Map<String, List<Server>> serversInZones = new HashMap<>(); // key为zone,val为lsrv中的Server
		setServerListForZones(serversInZones);
	}
	protected void setServerListForZones(Map<String, List<Server>> zoneServersMap) {
		getLoadBalancerStats().updateZoneServerMapping(zoneServersMap);
	}
}

DynamicServerListLoadBalancer的三个重要成员:ServerListServerListUpdaterServerListFilter

ServerList
interface ServerList<T extends Server> {
	List<T> getInitialListOfServers();	
	List<T> getUpdatedListOfServers();	
}

ServerList接口提供了两个方法分别获取初始化的服务实例清单和更新的服务实例清单。
经过查找,在SpringCloud的EurekaRibbonClientConfiguration中配置了ServerList的Bean,使用的实现类是DomainExtractingServerList

@Bean
public ServerList ribbonServerList(IClientConfig config, Provider<EurekaClient> eurekaClientProvider) {
	DiscoveryEnabledNIWSServerList discoveryServerList = new DiscoveryEnabledNIWSServerList(config, eurekaClientProvider);
	DomainExtractingServerList serverList = new DomainExtractingServerList(discoveryServerList, config, this.approximateZoneFromHostname);	// 使用DomainExtractingServerList作为实现类
	return serverList;
}

DomainExtractingServerList对父类的两个getListOfServers方法的实现,委托给了内部又定义的ServerList,并将内部ServerList返回的服务实例清单交由setZones方法处理、转成DomainExtractingServerList内部定义的DomainExtractingServer后返回。内部定义的ServerList使用了DiscoveryEnabledNIWSServerList,并由构造函数传入。

class DomainExtractingServerList implements ServerList<DiscoveryEnabledServer> {
	ServerList<DiscoveryEnableServer> list;
	@Override
	public List<DiscoveryEnabledServer> getInitialListOfServers() {
		return setZones(this.list.getInitialListOfServers()); // 委托给内部的ServerList
	}
	@Override
	public List<DiscoveryEnabledServer> getUpdatedListOfServers() {
		return setZones(this.list.getUpdatedListOfServers()); // 委托给内部的ServerList
	}
	private List<DiscoveryEnabledServer> setZones(List<DiscoveryEnabledServer> servers) {
		List<DiscoveryEnabledServer> result = new ArrayList<>();
		for	(DiscoveryEnabledServer server : servers) {
			result.add(new DomainExtractingServer(server, isSecure, shouldUseIpAddr, this.approximateZoneFromHostname));  // 将传入的DiscoveryEnabledServer转成DomainExtractingServer——是其子类
		}
		return result;
	}
}

DomainExtractingServerList的两个getListOfServers方法委托给了DiscoveryEnabledNIWSServerList类。
DiscoveryEnabledNIWSServerList的两个getListOfServers方法的实现都是调用obtainServersViaDiscovery方法实现的,即通过Eureka的服务发现机制获取服务实例InstanceInfo,并转成Server类型。

class DiscoveryEnabledNIWSServerList extends AbstractServerList<DiscoveryEnabledServer> {
	@Override
	public List<DiscoveryEnabledServer> getInitialListOfServers() { return obtainServersViaDiscovery(); }
	@Override
	public List<DiscoveryEnabledServer> getUpdatedListOfServers() { return obtainServersViaDiscovery(); }
	private List<DiscoveryEnabledServer> obtainServersViaDiscovery() {
		List<DiscoveryEnabledServer> serverList = new ArrayList<>();
		EurekaClient eurekaClient = eurekaClientProvider.get();
		for	(String vipAddress : vipAddresses.split(",")) {
			List<InstanceInfo> listOfInstanceInfo = eurekaClient.getInstancesByVipAddress(vipAddress, isSecure, targetRegion); // 通过EurekaClient从服务注册中心获取服务实例清单
			for (InstanceInfo ii : listOfInstanceInfo) {
				if (ii.getStatus().equals(InstanceStatus.UP)) { // 筛选状态为UP的服务实例
					DiscoveryEnabledServer des = createServer(ii, isSecure, shouldUseIpAddr); // 转成DiscoveryEnabledServer 
					serverList.add(des);
				}
			}
			if (serverList.size() > 0 && prioritizeVipAddressBasedServers) { break; }
		}
		return serverList;
	}
}
ServerListUpdater

通过实现ServerListUpdater接口,为DynamicServerListLoadBalancer提供相应的动态更新服务实例清单的策略。
其内部还有一个接口UpdateAction,通过实现UpdateAction来定义更新服务实例清单的具体操作。
ServerListUpdater接口定义了开始、停止更新的方法和获取更新信息的方法。

interface ServerListUpdater {

	public interface UpdateAction { void doUpdate(); } // 定义了更新的具体操作
	
	void start(UpdateAction updateAction);
	void stop();
	String getLastUpdate();
	long getDurationSinceLastUpdateMs();
	int getNumberMissedCycles();
	int getCoreThreads();
}

DynamicServerListLoadBalancer的一个标记了@Deprecated的构造函数中,传入了PollingServerListUpdater实现类,该实现类提供了动态更新服务清单的默认策略。

class PollingServerListUpdater implements ServerListUpdater {
	private static long LISTOFSERVERS_CACHE_UPDATE_DELAY = 1000; // 1s后
	private static int LISTOFSERVERS_CACHE_REPEAT_INTERVAL = 30 * 1000; // 每30s
	private final AtomicBoolean isActive = new AtomicBoolean(false);
	@Override
	public synchronized void start(final UpdateAction updateAction) {
		if (isActive.compareAndSet(false, true)) { // isActive为false时更新,并设为true
			Runnable wrapperRunnable = () -> {
				if (!isActive.get()) { // 如果被置为false,则中断
					if (scheduledFuture != null) { scheduledFuture.cancel(true); }
					return;
				}
				updateAction.doUpdate(); // 由UpdateAction执行更新操作
				lastUpdated = System.currentTimeMillis(); // 记录更新时间
			};
			// 定时任务的方式进行更新,默认1s后启动更新,每隔30s更新一次
			scheduledFuture = LasyHolder._serverListRefreshExecutor.scheduleWithFixedDelay(
				wrapperRunnable, initialDelayMs, refreshIntervalMs, TimeUnit.MILLISECONDS);
		}
	}
	@Override
	public synchronized void stop() {
		if (isActive.compareAndSet(true, false)) {
			scheduledFuture.cancel(true); // stop方法负责取消定时更新任务
		}
	}
}

ServerListUpdater的另一个实现类EurekaNotificationServerListUpdater中,使用了基于Eureka的事件监听机制来更新服务清单。

class EurekaNotificationServerListUpdater implements ServerListUpdater {
	@Override
	public synchronized void start(final UpdateAction updateAction) {
		if (isActive.compareAndSet(false, true)) {
			this.updateListener = () -> {
				if (event instanceof CacheRefreshedEvent) { // 获取Eureka的事件
					if (!refreshExecutor.isShutdown()) {
						refreshExecutor.submit(() -> {
							updateAction.doUpdate();  // 通过Eureka事件来触发服务清单的更新
							lastUpdated.set(System.currentTimeMillis());
						});
					}
				}
			};
			eurekaClient.registerEventListener(updateListener); // 将事件监听器注册
		}
	}
	@Override
	public synchronized void stop() {
		if (isActive.compareAndSet(true, false)) {
			eurekaClient.unregisterEventListener(updateListener); // 取消事件监听器,也就取消了事件驱动的服务更新
		}
	}
}
ServerListFilter

ServerListFilter定义了过滤服务清单的操作,返回过滤后的服务清单。

interface ServerListFilter<T extends Server> {
	List<T> getFilteredListOfServers(List<T> servers);
}

AbstractServerListFilterServerListFilter的直接抽象实现类,只定义了LoadBalancerStats对象,没有其他操作。
ZoneAffinityServerListFilter继承自AbstractServerListFilter,实现了基于ZoneAffinity的服务清单过滤,过滤掉与消费者不处于同一Zone的服务实例。

class ZoneAffinityServerListFilter<T extends Server> extends AbstractServerListFilter {
	private volatile boolean zoneAffinity = false;
	private volatile boolean zoneExclusive = false;
	private DynamicDoubleProperty activeRequestsPerServerThreshold; // 默认为0.6d
	private DynamicDoubleProperty blackOutServerPercentageThreshold; // 默认为0.8d
	private DynamicIntProperty availableServersThreshold; // 默认为2
	private ZoneAffinityPredicate zoneAffinityPredicate = new ZoneAffinityPredicate();

	@Override
	public List<T> getFilteredListOfServers(List<T> severs) {
		if (zone != null && (zoneAffinity || zoneExclusive) && servers != null && servers.size() > 0) {
			List<T> filteredServers = Lists.newArrayList(Iterables.filter(servers, this.zoneAffinityPredicate.getServerOnlyPredicate())); // 由zoneAffinityPredicate定义的过滤规则进行过滤,该规则只保留与消费者的zone相同的服务实例
			if (shouldEnableZoneAffinity(filteredServers)) { // 再由shouldEnableZoneAffinity方法决定是否启用过滤
				return filteredServers;
			} else if (zoneAffinity) {
				overrideCounter.increment();
			}
		}
		return servers;
	}
	private boolean shouldEnableZoneAffinity(List<T> filtered) {
		// 综合zoneAffinity、zoneExclusive的值,
		// 以及从LoadBalancerStats得到的几个指标值loadPerServer、instanceCount、circuitBreakerTrippedCount经过计算后与默认阈值activeRequestsPerServerThreshold、blackOutServerPercentageThreshold、availableServersThreshold的比较得出
	}
}
class ZoneAffinityPredicate extends AbstractServerPredicate {
	private final String zone = ConfigurationManager.getDeploymentContext().getValue(ContextKey.zone);
	@Override
	public boolean apply(PredicateKey input) {
		Server s = input.getServer();
		String az = s.getZone();
		if (az != null && zone != null && az.toLowerCase().equals(zone.toLowerCase())) {
			return true; // zone相同,返回true
		} else {
			return false;
		}
	}
}

ZoneAwareLoadBalancer

Load balancer that can avoid a zone as a whole when choosing server.
ZoneAwareLoadBalancer继承自DynamicServerListLoadBalancer,并重写了如下方法:

  • setServerListForZones——DynamicServerListLB定义,委托LoadBalancerStats实现。
  • chooseServer——BaseLoadBalancer委托IRule实现DynamicServerListLB未重写
setServersList与setServerListForZones

setServersListBaseLoadBalancer中定义,会用入参lsrv覆盖allServerList
DynamicServerListLoadBalancer中的setServerList,先调用super.setServersList执行覆盖逻辑,然后构造Map> serversInZones,作为入参调用setServerListForZones更新每个zone的stats信息
ZoneAwareLoadBalancer中,不再重写setServerList,只重写setServerListForZones且其中先调用了super.setServerListForZones,即仍执行覆盖逻辑、仍更新每个zone的stats信息,之后就是新增加的更新serverList的逻辑

class ZoneAwareLoadBalancer<T extends Server> extends DynamicServerListLoadBalancer<T> {
	// balancers用于存储zone -> LB 的对应关系
	private ConcurrentHashMap<String, BaseLoadBalancer> balancers = new ConcurrentHashMap<>();

	@Override
	protected void setServerListForZones(Map<String, List<Server>> zoneServersMap) { // key为zone,val为List
		super.setServerListForZones(zoneServersMap);
		if (balancers == null) { balancers = new ConcurrentHashMap<>(); }
		for (Map.Entry<String, List<Server>> entry : zoneServerMap.entrySet()) {
			String zone = entry.getKey().toLowerCase();
			// getLoadBalancer(zone)从balancers中取出对应zone的BaseLoadBalancer,如果为null,则初始化
			// 然后再调用lb的setServersList更新服务清单,此时的更新只针对当前zone
			getLoadBalancer(zone).setServersList(entry.getValue()); 
		}
		for (Map.Entry<String, BaseLoadBalancer> existingLBEntry : balancers.entrySet()) {
			if (!zoneServersMap.keySet().contains(existingLBEntry.getKey())) { // balancers的key即zone,如果不在传入的zoneServersMap中,则认为该zone下已没有服务
				// 所以将该zone对应的serverList清空,清空操作通过调用对应的LB实现,如此一来过时的zone不会被纳入选择服务节点的过程
				existingLBEntry.getValue().setServersList(Collections.emptyList());
			}
		}
	}
}
chooseServer

chooseServerBaseLoadBalancer中的实现采用了委托给IRulechoose方法的方式
DynamicServerListLoadBalancer仍沿用了委托给IRule的方式,没有重写
ZoneAwareLoadBalancer重写了chooseServer的实现,分为三步:

  • 如果未启用或zone的个数小于等于1的时候,则返回父类的chooseServer
  • 调用createSnapshot为所有zone创建快照,调用getAvailableZones筛选可用zone,从中随机选择一个zone,然后用其相应的LB选择一个server
  • 若根据上步能够选出server,则返回,否则仍返回父类的chooseServer
@Override
Server chooseServer(Object key) {
	if (!ENABLED.get() || getLoadBalancerStats().getAvailableZones.size() <= 1) { return super.chooseServer(key); }
	Server server = null;
	LoadBalancerStats lbStats = getLoadBalancerStats();
	Map<String, ZoneSnapshot> zoneSnapshot = ZoneAvoidanceRule.createSnapshot(lbStats);
	// triggeringLoad默认值0.2,triggeringBlackoutPercentage默认值0.99999
	Set<String> availableZones = ZoneAvoidanceRule.getAvailableZones(zoneSnapshot, triggeringLoad.get(), triggeringBlackoutPercentage.get());
	if (availableZones != null && availableZones.size() < zoneSnapshot.keySet().size()) {
		String zone = ZoneAvoidanceRule.randomChooseZone(zoneSnapshot, availableZones);
		if (zone != null) {
			BaseLoadBalancer zoneLoadBalancer = getLoadBalancer(zone);
			server = zoneLoadBalancer.chooseServer(key);
		}
	}
	return server != null ? server : super.chooseServer(key);
} 

你可能感兴趣的:(SpringCloud)