package netflix.ocelli.loadbalancer; import netflix.ocelli.LoadBalancerStrategy; import java.util.Comparator; import java.util.List; import java.util.NoSuchElementException; import java.util.Random; /** * This selector chooses 2 random hosts and picks the host with the 'best' * performance where that determination is deferred to a customizable function. * * This implementation is based on the paper 'The Power of Two Choices in * Randomized Load Balancing' http://www.eecs.harvard.edu/~michaelm/postscripts/tpds2001.pdf * This paper states that selecting the best of 2 random servers results in an * exponential improvement over selecting a single random node (also includes * round robin) but that adding a third (or more) servers does not yield a significant * performance improvement. * * @author elandau * * @param <T> */ public class ChoiceOfTwoLoadBalancer<T> implements LoadBalancerStrategy<T> { public static <T> ChoiceOfTwoLoadBalancer<T> create(final Comparator<T> func) { return new ChoiceOfTwoLoadBalancer<T>(func); } private final Comparator<T> func; private final Random rand = new Random(); public ChoiceOfTwoLoadBalancer(final Comparator<T> func2) { func = func2; } /** * @throws NoSuchElementException */ @Override public T choose(List<T> candidates) { if (candidates.isEmpty()) { throw new NoSuchElementException("No servers available in the load balancer"); } else if (candidates.size() == 1) { return candidates.get(0); } else { int pos = rand.nextInt(candidates.size()); T first = candidates.get(pos); T second = candidates.get((rand.nextInt(candidates.size()-1) + pos + 1) % candidates.size()); return func.compare(first, second) >= 0 ? first : second; } } }