/* * * Copyright 2013 Netflix, Inc. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. * */ package com.netflix.loadbalancer; import java.util.List; import java.util.Random; import java.util.concurrent.atomic.AtomicInteger; import javax.annotation.Nullable; import com.google.common.base.Optional; import com.google.common.base.Predicate; import com.google.common.collect.ImmutableList; import com.google.common.collect.Iterables; import com.google.common.collect.Lists; import com.netflix.client.config.IClientConfig; import com.netflix.loadbalancer.PredicateKey; /** * A basic building block for server filtering logic which can be used in rules and server list filters. * The input object of the predicate is {@link PredicateKey}, which has Server and load balancer key * information. Therefore, it is possible to develop logic to filter servers by both Server and load balancer * key or either one of them. * * @author awang * */ public abstract class AbstractServerPredicate implements Predicate<PredicateKey> { protected IRule rule; private volatile LoadBalancerStats lbStats; private final Random random = new Random(); private final AtomicInteger nextIndex = new AtomicInteger(); private final Predicate<Server> serverOnlyPredicate = new Predicate<Server>() { @Override public boolean apply(@Nullable Server input) { return AbstractServerPredicate.this.apply(new PredicateKey(input)); } }; public static AbstractServerPredicate alwaysTrue() { return new AbstractServerPredicate() { @Override public boolean apply(@Nullable PredicateKey input) { return true; } }; } public AbstractServerPredicate() { } public AbstractServerPredicate(IRule rule) { this.rule = rule; } public AbstractServerPredicate(IRule rule, IClientConfig clientConfig) { this.rule = rule; } public AbstractServerPredicate(LoadBalancerStats lbStats, IClientConfig clientConfig) { this.lbStats = lbStats; } protected LoadBalancerStats getLBStats() { if (lbStats != null) { return lbStats; } else if (rule != null) { ILoadBalancer lb = rule.getLoadBalancer(); if (lb instanceof AbstractLoadBalancer) { LoadBalancerStats stats = ((AbstractLoadBalancer) lb).getLoadBalancerStats(); setLoadBalancerStats(stats); return stats; } else { return null; } } else { return null; } } public void setLoadBalancerStats(LoadBalancerStats stats) { this.lbStats = stats; } /** * Get the predicate to filter list of servers. The load balancer key is treated as null * as the input of this predicate. */ public Predicate<Server> getServerOnlyPredicate() { return serverOnlyPredicate; } /** * Get servers filtered by this predicate from list of servers. Load balancer key * is presumed to be null. * * @see #getEligibleServers(List, Object) * */ public List<Server> getEligibleServers(List<Server> servers) { return getEligibleServers(servers, null); } /** * Get servers filtered by this predicate from list of servers. */ public List<Server> getEligibleServers(List<Server> servers, Object loadBalancerKey) { if (loadBalancerKey == null) { return ImmutableList.copyOf(Iterables.filter(servers, this.getServerOnlyPredicate())); } else { List<Server> results = Lists.newArrayList(); for (Server server: servers) { if (this.apply(new PredicateKey(loadBalancerKey, server))) { results.add(server); } } return results; } } /** * Choose a random server after the predicate filters a list of servers. Load balancer key * is presumed to be null. * */ public Optional<Server> chooseRandomlyAfterFiltering(List<Server> servers) { List<Server> eligible = getEligibleServers(servers); if (eligible.size() == 0) { return Optional.absent(); } return Optional.of(eligible.get(random.nextInt(eligible.size()))); } /** * Choose a server in a round robin fashion after the predicate filters a list of servers. Load balancer key * is presumed to be null. */ public Optional<Server> chooseRoundRobinAfterFiltering(List<Server> servers) { List<Server> eligible = getEligibleServers(servers); if (eligible.size() == 0) { return Optional.absent(); } return Optional.of(eligible.get(nextIndex.getAndIncrement() % eligible.size())); } /** * Choose a random server after the predicate filters list of servers given list of servers and * load balancer key. * */ public Optional<Server> chooseRandomlyAfterFiltering(List<Server> servers, Object loadBalancerKey) { List<Server> eligible = getEligibleServers(servers, loadBalancerKey); if (eligible.size() == 0) { return Optional.absent(); } return Optional.of(eligible.get(random.nextInt(eligible.size()))); } /** * Choose a server in a round robin fashion after the predicate filters a given list of servers and load balancer key. */ public Optional<Server> chooseRoundRobinAfterFiltering(List<Server> servers, Object loadBalancerKey) { List<Server> eligible = getEligibleServers(servers, loadBalancerKey); if (eligible.size() == 0) { return Optional.absent(); } return Optional.of(eligible.get(nextIndex.getAndIncrement() % eligible.size())); } /** * Create an instance from a predicate. */ public static AbstractServerPredicate ofKeyPredicate(final Predicate<PredicateKey> p) { return new AbstractServerPredicate() { @Override @edu.umd.cs.findbugs.annotations.SuppressWarnings(value = "NP") public boolean apply(PredicateKey input) { return p.apply(input); } }; } /** * Create an instance from a predicate. */ public static AbstractServerPredicate ofServerPredicate(final Predicate<Server> p) { return new AbstractServerPredicate() { @Override @edu.umd.cs.findbugs.annotations.SuppressWarnings(value = "NP") public boolean apply(PredicateKey input) { return p.apply(input.getServer()); } }; } }