所谓负载均衡,就是为资源分配负载,就是选择合适的服务处理请求。
在SpringCloud
的客户端负载均衡器LoadBalancerClient
的默认实现RibbonLoadBalancerClient
中,使用了Ribbon
的ILoadBalancer
接口来实现chooseServer
即负载均衡功能。
// ILoadBalancer接口定义了负载均衡器的操作,包括初始化服务列表、选择服务实例、关停服务、获取服务列表等
interface ILoadBalancer {
void addServers(List<Server> newServers);
Server chooseServer(Object key);
void markServerDown(Server server);
List<Server> getReachableServers();
List<Server> getAllServers();
}
ILoadBalancer
的直接实现类是抽象类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();
}
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
继承自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
的三个重要成员:ServerList
,ServerListUpdater
,ServerListFilter
。
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
接口,为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
定义了过滤服务清单的操作,返回过滤后的服务清单。
interface ServerListFilter<T extends Server> {
List<T> getFilteredListOfServers(List<T> servers);
}
AbstractServerListFilter
是ServerListFilter
的直接抽象实现类,只定义了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;
}
}
}
Load balancer that can avoid a zone as a whole when choosing server.
ZoneAwareLoadBalancer
继承自DynamicServerListLoadBalancer
,并重写了如下方法:
setServerListForZones
——DynamicServerListLB
定义,委托LoadBalancerStats
实现。chooseServer
——BaseLoadBalancer
委托IRule实现
,DynamicServerListLB
未重写setServersList
在BaseLoadBalancer
中定义,会用入参lsrv覆盖allServerList
在DynamicServerListLoadBalancer
中的setServerList
,先调用super.setServersList
执行覆盖逻辑,然后构造Map
,作为入参调用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
在BaseLoadBalancer
中的实现采用了委托给IRule
的choose
方法的方式
DynamicServerListLoadBalancer
仍沿用了委托给IRule
的方式,没有重写
ZoneAwareLoadBalancer
重写了chooseServer
的实现,分为三步:
chooseServer
createSnapshot
为所有zone创建快照,调用getAvailableZones
筛选可用zone,从中随机选择一个zone,然后用其相应的LB选择一个serverchooseServer
@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);
}