diff --git a/spring-cloud-loadbalancer/src/main/java/org/springframework/cloud/loadbalancer/core/LazyWeightedServiceInstanceList.java b/spring-cloud-loadbalancer/src/main/java/org/springframework/cloud/loadbalancer/core/LazyWeightedServiceInstanceList.java index 2d33c924..6a21b856 100644 --- a/spring-cloud-loadbalancer/src/main/java/org/springframework/cloud/loadbalancer/core/LazyWeightedServiceInstanceList.java +++ b/spring-cloud-loadbalancer/src/main/java/org/springframework/cloud/loadbalancer/core/LazyWeightedServiceInstanceList.java @@ -32,14 +32,14 @@ import org.springframework.cloud.client.ServiceInstance; */ class LazyWeightedServiceInstanceList extends AbstractList { - private final InterleavedWeightedServiceInstanceSelector selector; - - private volatile int position; - /* for testing */ final ServiceInstance[] expanded; private final Object expandingLock = new Object(); + private WeightedServiceInstanceSelector selector; + + private volatile int position = 0; + LazyWeightedServiceInstanceList(List instances, int[] weights) { // Calculate the greatest common divisor (GCD) of weights, and the // total number of elements after expansion. @@ -49,10 +49,8 @@ class LazyWeightedServiceInstanceList extends AbstractList { greatestCommonDivisor = greatestCommonDivisor(greatestCommonDivisor, weight); total += weight; } - selector = new InterleavedWeightedServiceInstanceSelector(instances.toArray(new ServiceInstance[0]), weights, - greatestCommonDivisor); - position = 0; expanded = new ServiceInstance[total / greatestCommonDivisor]; + selector = new WeightedServiceInstanceSelector(instances, weights, greatestCommonDivisor); } @Override @@ -62,6 +60,9 @@ class LazyWeightedServiceInstanceList extends AbstractList { for (; position <= index && position < expanded.length; position++) { expanded[position] = selector.next(); } + if (position == expanded.length) { + selector = null; // for gc + } } } return expanded[index]; @@ -82,77 +83,61 @@ class LazyWeightedServiceInstanceList extends AbstractList { return a; } - static class InterleavedWeightedServiceInstanceSelector { - - static final int MODE_LIST = 0; - - static final int MODE_QUEUE = 1; - - final ServiceInstance[] instances; - - final int[] weights; - - final int greatestCommonDivisor; - - final Queue queue; + static class WeightedServiceInstanceSelector { - int mode; + Queue active; - int position; + Queue expired; - InterleavedWeightedServiceInstanceSelector(ServiceInstance[] instances, int[] weights, - int greatestCommonDivisor) { - this.instances = instances; - this.weights = weights; - this.greatestCommonDivisor = greatestCommonDivisor; - queue = new ArrayDeque<>(instances.length); - mode = MODE_LIST; - position = 0; + WeightedServiceInstanceSelector(List instances, int[] weights, int greatestCommonDivisor) { + active = new ArrayDeque<>(instances.size()); + expired = new ArrayDeque<>(instances.size()); + // Use iterator for some implementation of the List that not supports + // RandomAccess, but `weights` is supported, so use a local variable `i` + // to get the current position. + int i = 0; + for (ServiceInstance instance : instances) { + active.offer(new Entry(instance, weights[i] / greatestCommonDivisor)); + i++; + } } ServiceInstance next() { - if (mode == MODE_LIST) { - ServiceInstance instance = instances[position]; - int weight = weights[position]; - - weight = weight - greatestCommonDivisor; - if (weight > 0) { - queue.add(new Entry(instance, weight)); - } + if (active.isEmpty()) { + Queue temp = active; + active = expired; + expired = temp; + } - position++; - if (position == instances.length) { - mode = MODE_QUEUE; - position = 0; - } + Entry entry = active.poll(); + if (entry == null) { + // Suppress warnings, never touched! + return null; + } - return instance; + entry.remainder--; + if (entry.remainder == 0) { + entry.remainder = entry.weight; + expired.offer(entry); } else { - if (queue.isEmpty()) { - mode = MODE_LIST; - return next(); - } - - Entry entry = queue.poll(); - entry.weight = entry.weight - greatestCommonDivisor; - if (entry.weight > 0) { - queue.add(entry); - } - - return entry.instance; + active.offer(entry); } + return entry.instance; } static class Entry { final ServiceInstance instance; - int weight; + final int weight; + + int remainder; Entry(ServiceInstance instance, int weight) { this.instance = instance; this.weight = weight; + remainder = weight; } }