1717package org .springframework .cloud .loadbalancer .core ;
1818
1919import java .util .AbstractList ;
20+ import java .util .ArrayDeque ;
2021import java .util .List ;
22+ import java .util .Queue ;
2123
2224import org .springframework .cloud .client .ServiceInstance ;
2325
3032 */
3133class LazyWeightedServiceInstanceList extends AbstractList <ServiceInstance > {
3234
33- private final List <ServiceInstance > instances ;
34-
35- private final int [] weights ;
36-
37- private SmoothServiceInstanceSelector selector ;
35+ private final InterleavedWeightedServiceInstanceSelector selector ;
3836
3937 private volatile int position ;
4038
41- /* for testing */ ServiceInstance [] expanded ;
39+ /* for testing */ final ServiceInstance [] expanded ;
4240
4341 private final Object expandingLock = new Object ();
4442
4543 LazyWeightedServiceInstanceList (List <ServiceInstance > instances , int [] weights ) {
46- this .instances = instances ;
47- this .weights = weights ;
48- this .init ();
49- }
50-
51- private void init () {
52- // Calculate the greatest common divisor (GCD) of weights, max weight and the
44+ // Calculate the greatest common divisor (GCD) of weights, and the
5345 // total number of elements after expansion.
5446 int greatestCommonDivisor = 0 ;
55- int max = 0 ;
5647 int total = 0 ;
5748 for (int weight : weights ) {
5849 greatestCommonDivisor = greatestCommonDivisor (greatestCommonDivisor , weight );
59- max = Math .max (max , weight );
6050 total += weight ;
6151 }
62- selector = new SmoothServiceInstanceSelector (instances , weights , max , greatestCommonDivisor );
52+ selector = new InterleavedWeightedServiceInstanceSelector (instances .toArray (new ServiceInstance [0 ]), weights ,
53+ greatestCommonDivisor );
6354 position = 0 ;
6455 expanded = new ServiceInstance [total / greatestCommonDivisor ];
6556 }
6657
6758 @ Override
6859 public ServiceInstance get (int index ) {
69- if (index < position ) {
70- return expanded [index ];
71- }
72- synchronized (expandingLock ) {
73- for (; position <= index && position < expanded .length ; position ++) {
74- expanded [position ] = selector .next ();
60+ if (index >= position ) {
61+ synchronized (expandingLock ) {
62+ for (; position <= index && position < expanded .length ; position ++) {
63+ expanded [position ] = selector .next ();
64+ }
7565 }
7666 }
7767 return expanded [index ];
@@ -92,44 +82,79 @@ static int greatestCommonDivisor(int a, int b) {
9282 return a ;
9383 }
9484
95- static class SmoothServiceInstanceSelector {
85+ static class InterleavedWeightedServiceInstanceSelector {
86+
87+ static final int MODE_LIST = 0 ;
9688
97- final List <ServiceInstance > instances ;
89+ static final int MODE_QUEUE = 1 ;
90+
91+ final ServiceInstance [] instances ;
9892
9993 final int [] weights ;
10094
101- final int maxWeight ;
95+ final int greatestCommonDivisor ;
10296
103- final int gcd ;
97+ final Queue < Entry > queue ;
10498
105- int position ;
99+ int mode ;
106100
107- int currentWeight ;
101+ int position ;
108102
109- SmoothServiceInstanceSelector (List <ServiceInstance > instances , int [] weights , int maxWeight , int gcd ) {
103+ InterleavedWeightedServiceInstanceSelector (ServiceInstance [] instances , int [] weights ,
104+ int greatestCommonDivisor ) {
110105 this .instances = instances ;
111106 this .weights = weights ;
112- this .maxWeight = maxWeight ;
113- this .gcd = gcd ;
114- this .currentWeight = 0 ;
107+ this .greatestCommonDivisor = greatestCommonDivisor ;
108+ queue = new ArrayDeque <>(instances .length );
109+ mode = MODE_LIST ;
110+ position = 0 ;
115111 }
116112
117113 ServiceInstance next () {
118- // The weight of all instances is greater than 0, so it must be able to exit
119- // the loop.
120- while (true ) {
121- for (int picked = position ; picked < weights .length ; picked ++) {
122- if (weights [picked ] > currentWeight ) {
123- position = picked + 1 ;
124- return instances .get (picked );
125- }
114+ if (mode == MODE_LIST ) {
115+ ServiceInstance instance = instances [position ];
116+ int weight = weights [position ];
117+
118+ weight = weight - greatestCommonDivisor ;
119+ if (weight > 0 ) {
120+ queue .add (new Entry (instance , weight ));
126121 }
127- position = 0 ;
128- currentWeight = currentWeight + gcd ;
129- if (currentWeight >= maxWeight ) {
130- currentWeight = 0 ;
122+
123+ position ++;
124+ if (position == instances .length ) {
125+ mode = MODE_QUEUE ;
126+ position = 0 ;
131127 }
128+
129+ return instance ;
132130 }
131+ else {
132+ if (queue .isEmpty ()) {
133+ mode = MODE_LIST ;
134+ return next ();
135+ }
136+
137+ Entry entry = queue .poll ();
138+ entry .weight = entry .weight - greatestCommonDivisor ;
139+ if (entry .weight > 0 ) {
140+ queue .add (entry );
141+ }
142+
143+ return entry .instance ;
144+ }
145+ }
146+
147+ static class Entry {
148+
149+ final ServiceInstance instance ;
150+
151+ int weight ;
152+
153+ Entry (ServiceInstance instance , int weight ) {
154+ this .instance = instance ;
155+ this .weight = weight ;
156+ }
157+
133158 }
134159
135160 }
0 commit comments