Talk about ZoneAffinityServerListFilter of eureka

  springcloud

Order

This article mainly studies the ZoneAffinityServerListFilter of eureka

ZoneAffinityServerListFilter

ribbon-loadbalancer-2.2.5-sources.jar! /com/netflix/loadbalancer/ZoneAffinityServerListFilter.java

/**
 * 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;
    }   
}

You can see that zoneaffinitypredicate. getservernalypredicate () is called here first to filter; Then call shouldEnableZoneAffinity to judge whether the filtered data really needs to be returned.

ZoneAffinityPredicate

ribbon-loadbalancer-2.2.5-sources.jar! /com/netflix/loadbalancer/ZoneAffinityPredicate.java

/**
 * 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;
        }
    }
}

It can be seen that the server’s zone is judged here, and the same zone is selected.

shouldEnableZoneAffinity

    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;
            }
            
        }
    }

It is judged here that if the server statistics of the target zone are not too good to meet the disconnection standard, the server of the zone will not be returned.

ZonePreferenceServerListFilter

spring-cloud-netflix-ribbon-2.0.0.RC1-sources.jar! /org/springframework/cloud/netflix/ribbon/ZonePreferenceServerListFilter.java

/**
 * 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;
    }
}

The ZonePreferenceServerListFilter of Spring Cloud inherits the zoneaffinitiyserverlistfilter of eureka and rewrites the getFilteredListOfServers method. that is, if eureka’s zoneaffinitiyserverlistfilter does not filter according to the zone, then it will filter again and select the server with the same zone as the instance. Note that judgment has been made here. If the filter is empty according to the zone, the server filtered by the parent class will be returned, i.e. the filter will no longer be performed according to the zone.

Summary

Eureka provides ZoneAffinityServerListFilter, which can perform zone affinity filtering on the server. at the same time, it will also judge whether the server after zone affinity filtering needs to be used according to the server’s health statistics. if it should not be opened, it will return the list of servers without filtering. For example, if there is no server list of the same zone as this instance, it is obvious that the empty list filtered according to the zone should not be returned.

doc