本文主要研究一下eureka的ZoneAffinityServerListFilterjava
ribbon-loadbalancer-2.2.5-sources.jar!/com/netflix/loadbalancer/ZoneAffinityServerListFilter.javaspring
/** * This server list filter deals with filtering out servers based on the Zone affinity. * This filtering will be turned on if either {@link CommonClientConfigKey#EnableZoneAffinity} * or {@link CommonClientConfigKey#EnableZoneExclusivity} is set to true in {@link IClientConfig} object * passed into this class during initialization. When turned on, servers outside the same zone (as * indicated by {@link Server#getZone()}) will be filtered out. By default, zone affinity * and exclusivity are turned off and nothing is filtered out. * * @author stonse * */ public class ZoneAffinityServerListFilter<T extends Server> extends AbstractServerListFilter<T> implements IClientConfigAware { //...... @Override public List<T> getFilteredListOfServers(List<T> servers) { if (zone != null && (zoneAffinity || zoneExclusive) && servers !=null && servers.size() > 0){ List<T> filteredServers = Lists.newArrayList(Iterables.filter( servers, this.zoneAffinityPredicate.getServerOnlyPredicate())); if (shouldEnableZoneAffinity(filteredServers)) { return filteredServers; } else if (zoneAffinity) { overrideCounter.increment(); } } return servers; } }
能够看到这里首先调用了zoneAffinityPredicate.getServerOnlyPredicate()进行过滤;而后再调用shouldEnableZoneAffinity判断是否真的须要返回过滤后的数据。
ribbon-loadbalancer-2.2.5-sources.jar!/com/netflix/loadbalancer/ZoneAffinityPredicate.javaapp
/** * A predicate the filters out servers that are not in the same zone as the client's current * zone. The current zone is determined from the call * * <pre>{@code * ConfigurationManager.getDeploymentContext().getValue(ContextKey.zone); * }</pre> * * @author awang * */ public class ZoneAffinityPredicate extends AbstractServerPredicate { private final String zone = ConfigurationManager.getDeploymentContext().getValue(ContextKey.zone); public ZoneAffinityPredicate() { } @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; } else { return false; } } }
能够看到这里对server的zone进行判断,相同的zone才选取出来。
private boolean shouldEnableZoneAffinity(List<T> filtered) { if (!zoneAffinity && !zoneExclusive) { return false; } if (zoneExclusive) { return true; } LoadBalancerStats stats = getLoadBalancerStats(); if (stats == null) { return zoneAffinity; } else { logger.debug("Determining if zone affinity should be enabled with given server list: {}", filtered); ZoneSnapshot snapshot = stats.getZoneSnapshot(filtered); double loadPerServer = snapshot.getLoadPerServer(); int instanceCount = snapshot.getInstanceCount(); int circuitBreakerTrippedCount = snapshot.getCircuitTrippedCount(); if (((double) circuitBreakerTrippedCount) / instanceCount >= blackOutServerPercentageThreshold.get() || loadPerServer >= activeReqeustsPerServerThreshold.get() || (instanceCount - circuitBreakerTrippedCount) < availableServersThreshold.get()) { logger.debug("zoneAffinity is overriden. blackOutServerPercentage: {}, activeReqeustsPerServer: {}, availableServers: {}", new Object[] {(double) circuitBreakerTrippedCount / instanceCount, loadPerServer, instanceCount - circuitBreakerTrippedCount}); return false; } else { return true; } } }
这里进行判断,若是目标zone的server统计数据不是太好,达到断路的标准,则不会返回该zone的server。
spring-cloud-netflix-ribbon-2.0.0.RC1-sources.jar!/org/springframework/cloud/netflix/ribbon/ZonePreferenceServerListFilter.javaide
/** * A filter that actively prefers the local zone (as defined by the deployment context, or * the Eureka instance metadata). * * @author Dave Syer */ public class ZonePreferenceServerListFilter extends ZoneAffinityServerListFilter<Server> { //...... @Override public List<Server> getFilteredListOfServers(List<Server> servers) { List<Server> output = super.getFilteredListOfServers(servers); if (this.zone != null && output.size() == servers.size()) { List<Server> local = new ArrayList<>(); for (Server server : output) { if (this.zone.equalsIgnoreCase(server.getZone())) { local.add(server); } } if (!local.isEmpty()) { return local; } } return output; } }
Spring Cloud的ZonePreferenceServerListFilter继承了eureka的ZoneAffinityServerListFilter,重写了getFilteredListOfServers方法,即eureka的ZoneAffinityServerListFilter计算出来没有根据zone过滤的话,那么它会再过滤一次,选出跟实例相同zone的server。注意这里进行了判断,如根据zone过滤出来为空,则返回父类过滤出来server,即再也不根据zone进行过滤。
eureka提供了ZoneAffinityServerListFilter,能够对server进行zone亲和性过滤,同时还会根据server的健康统计判断是否须要使用进行zone亲和过滤后的server,若是不该该开启,则返回没有过滤的server列表。好比没有跟该实例相同zone的server列表,那么很明显就是不该该返回根据zone进行过滤后的空列表。ui