24
24
import com .google .common .base .MoreObjects ;
25
25
import com .google .common .base .Preconditions ;
26
26
import com .google .common .collect .ImmutableList ;
27
+ import com .google .common .collect .Lists ;
27
28
import io .grpc .ConnectivityState ;
28
29
import io .grpc .ConnectivityStateInfo ;
29
30
import io .grpc .Deadline .Ticker ;
31
+ import io .grpc .DoubleHistogramMetricInstrument ;
30
32
import io .grpc .EquivalentAddressGroup ;
31
33
import io .grpc .ExperimentalApi ;
32
34
import io .grpc .LoadBalancer ;
33
35
import io .grpc .LoadBalancerProvider ;
36
+ import io .grpc .LongCounterMetricInstrument ;
37
+ import io .grpc .MetricInstrumentRegistry ;
34
38
import io .grpc .NameResolver ;
35
39
import io .grpc .Status ;
36
40
import io .grpc .SynchronizationContext ;
57
61
import java .util .logging .Logger ;
58
62
59
63
/**
60
- * A {@link LoadBalancer} that provides weighted-round-robin load-balancing over
61
- * the {@link EquivalentAddressGroup}s from the {@link NameResolver}. The subchannel weights are
64
+ * A {@link LoadBalancer} that provides weighted-round-robin load-balancing over the
65
+ * {@link EquivalentAddressGroup}s from the {@link NameResolver}. The subchannel weights are
62
66
* determined by backend metrics using ORCA.
63
67
*/
64
68
@ ExperimentalApi ("https://github.com/grpc/grpc-java/issues/9885" )
65
69
final class WeightedRoundRobinLoadBalancer extends RoundRobinLoadBalancer {
70
+
71
+ private static final LongCounterMetricInstrument RR_FALLBACK_COUNTER ;
72
+ private static final LongCounterMetricInstrument ENDPOINT_WEIGHT_NOT_YET_USEABLE_COUNTER ;
73
+ private static final LongCounterMetricInstrument ENDPOINT_WEIGHT_STALE_COUNTER ;
74
+ private static final DoubleHistogramMetricInstrument ENDPOINT_WEIGHTS_HISTOGRAM ;
66
75
private static final Logger log = Logger .getLogger (
67
76
WeightedRoundRobinLoadBalancer .class .getName ());
68
77
private WeightedRoundRobinLoadBalancerConfig config ;
@@ -74,6 +83,31 @@ final class WeightedRoundRobinLoadBalancer extends RoundRobinLoadBalancer {
74
83
private final long infTime ;
75
84
private final Ticker ticker ;
76
85
86
+ // The metric instruments are only registered once and shared by all instances of this LB.
87
+ static {
88
+ MetricInstrumentRegistry metricInstrumentRegistry
89
+ = MetricInstrumentRegistry .getDefaultRegistry ();
90
+ RR_FALLBACK_COUNTER = metricInstrumentRegistry .registerLongCounter ("grpc.lb.wrr.rr_fallback" ,
91
+ "Number of scheduler updates in which there were not enough endpoints with valid "
92
+ + "weight, which caused the WRR policy to fall back to RR behavior" , "update" ,
93
+ Lists .newArrayList ("grpc.target" ), Lists .newArrayList ("grpc.lb.locality" ), true );
94
+ ENDPOINT_WEIGHT_NOT_YET_USEABLE_COUNTER = metricInstrumentRegistry .registerLongCounter (
95
+ "grpc.lb.wrr.endpoint_weight_not_yet_usable" ,
96
+ "Number of endpoints from each scheduler update that don't yet have usable weight "
97
+ + "information" , "endpoint" , Lists .newArrayList ("grpc.target" ),
98
+ Lists .newArrayList ("grpc.lb.locality" ), true );
99
+ ENDPOINT_WEIGHT_STALE_COUNTER = metricInstrumentRegistry .registerLongCounter (
100
+ "grpc.lb.wrr.endpoint_weight_stale" ,
101
+ "Number of endpoints from each scheduler update whose latest weight is older than the "
102
+ + "expiration period" , "endpoint" , Lists .newArrayList ("grpc.target" ),
103
+ Lists .newArrayList ("grpc.lb.locality" ), true );
104
+ ENDPOINT_WEIGHTS_HISTOGRAM = metricInstrumentRegistry .registerDoubleHistogram (
105
+ "grpc.lb.wrr.endpoint_weights" , "The histogram buckets will be endpoint weight ranges." ,
106
+ "weight" , Lists .newArrayList (), Lists .newArrayList ("grpc.target" ),
107
+ Lists .newArrayList ("grpc.lb.locality" ),
108
+ true );
109
+ }
110
+
77
111
public WeightedRoundRobinLoadBalancer (Helper helper , Ticker ticker ) {
78
112
this (new WrrHelper (OrcaOobUtil .newOrcaReportingHelper (helper )), ticker , new Random ());
79
113
}
@@ -145,7 +179,7 @@ public Status acceptResolvedAddresses(ResolvedAddresses resolvedAddresses) {
145
179
@ Override
146
180
public SubchannelPicker createReadyPicker (Collection <ChildLbState > activeList ) {
147
181
return new WeightedRoundRobinPicker (ImmutableList .copyOf (activeList ),
148
- config .enableOobLoadReport , config .errorUtilizationPenalty , sequence );
182
+ config .enableOobLoadReport , config .errorUtilizationPenalty , sequence , getHelper () );
149
183
}
150
184
151
185
@ VisibleForTesting
@@ -163,16 +197,18 @@ public WeightedChildLbState(Object key, LoadBalancerProvider policyProvider, Obj
163
197
super (key , policyProvider , childConfig , initialPicker );
164
198
}
165
199
166
- private double getWeight () {
200
+ private double getWeight (AtomicInteger staleEndpoints , AtomicInteger notYetUsableEndpoints ) {
167
201
if (config == null ) {
168
202
return 0 ;
169
203
}
170
204
long now = ticker .nanoTime ();
171
205
if (now - lastUpdated >= config .weightExpirationPeriodNanos ) {
172
206
nonEmptySince = infTime ;
207
+ staleEndpoints .incrementAndGet ();
173
208
return 0 ;
174
209
} else if (now - nonEmptySince < config .blackoutPeriodNanos
175
210
&& config .blackoutPeriodNanos > 0 ) {
211
+ notYetUsableEndpoints .incrementAndGet ();
176
212
return 0 ;
177
213
} else {
178
214
return weight ;
@@ -336,10 +372,11 @@ static final class WeightedRoundRobinPicker extends SubchannelPicker {
336
372
private final float errorUtilizationPenalty ;
337
373
private final AtomicInteger sequence ;
338
374
private final int hashCode ;
375
+ private final LoadBalancer .Helper helper ;
339
376
private volatile StaticStrideScheduler scheduler ;
340
377
341
378
WeightedRoundRobinPicker (List <ChildLbState > children , boolean enableOobLoadReport ,
342
- float errorUtilizationPenalty , AtomicInteger sequence ) {
379
+ float errorUtilizationPenalty , AtomicInteger sequence , LoadBalancer . Helper helper ) {
343
380
checkNotNull (children , "children" );
344
381
Preconditions .checkArgument (!children .isEmpty (), "empty child list" );
345
382
this .children = children ;
@@ -353,6 +390,7 @@ static final class WeightedRoundRobinPicker extends SubchannelPicker {
353
390
this .enableOobLoadReport = enableOobLoadReport ;
354
391
this .errorUtilizationPenalty = errorUtilizationPenalty ;
355
392
this .sequence = checkNotNull (sequence , "sequence" );
393
+ this .helper = helper ;
356
394
357
395
// For equality we treat children as a set; use hash code as defined by Set
358
396
int sum = 0 ;
@@ -387,11 +425,37 @@ public PickResult pickSubchannel(PickSubchannelArgs args) {
387
425
388
426
private void updateWeight () {
389
427
float [] newWeights = new float [children .size ()];
428
+ AtomicInteger staleEndpoints = new AtomicInteger ();
429
+ AtomicInteger notYetUsableEndpoints = new AtomicInteger ();
390
430
for (int i = 0 ; i < children .size (); i ++) {
391
- double newWeight = ((WeightedChildLbState )children .get (i )).getWeight ();
431
+ double newWeight = ((WeightedChildLbState ) children .get (i )).getWeight (staleEndpoints ,
432
+ notYetUsableEndpoints );
433
+ // TODO: add target and locality labels once available
434
+ helper .getMetricRecorder ()
435
+ .recordDoubleHistogram (ENDPOINT_WEIGHTS_HISTOGRAM , newWeight , ImmutableList .of ("" ),
436
+ ImmutableList .of ("" ));
392
437
newWeights [i ] = newWeight > 0 ? (float ) newWeight : 0.0f ;
393
438
}
439
+ if (staleEndpoints .get () > 0 ) {
440
+ // TODO: add target and locality labels once available
441
+ helper .getMetricRecorder ()
442
+ .addLongCounter (ENDPOINT_WEIGHT_STALE_COUNTER , staleEndpoints .get (),
443
+ ImmutableList .of ("" ),
444
+ ImmutableList .of ("" ));
445
+ }
446
+ if (notYetUsableEndpoints .get () > 0 ) {
447
+ // TODO: add target and locality labels once available
448
+ helper .getMetricRecorder ()
449
+ .addLongCounter (ENDPOINT_WEIGHT_NOT_YET_USEABLE_COUNTER , notYetUsableEndpoints .get (),
450
+ ImmutableList .of ("" ), ImmutableList .of ("" ));
451
+ }
452
+
394
453
this .scheduler = new StaticStrideScheduler (newWeights , sequence );
454
+ if (this .scheduler .usesRoundRobin ()) {
455
+ // TODO: add target and locality labels once available
456
+ helper .getMetricRecorder ()
457
+ .addLongCounter (RR_FALLBACK_COUNTER , 1 , ImmutableList .of ("" ), ImmutableList .of ("" ));
458
+ }
395
459
}
396
460
397
461
@ Override
@@ -454,6 +518,7 @@ public boolean equals(Object o) {
454
518
static final class StaticStrideScheduler {
455
519
private final short [] scaledWeights ;
456
520
private final AtomicInteger sequence ;
521
+ private final boolean usesRoundRobin ;
457
522
private static final int K_MAX_WEIGHT = 0xFFFF ;
458
523
459
524
// Assuming the mean of all known weights is M, StaticStrideScheduler will clamp
@@ -494,8 +559,10 @@ static final class StaticStrideScheduler {
494
559
if (numWeightedChannels > 0 ) {
495
560
unscaledMeanWeight = sumWeight / numWeightedChannels ;
496
561
unscaledMaxWeight = Math .min (unscaledMaxWeight , (float ) (K_MAX_RATIO * unscaledMeanWeight ));
562
+ usesRoundRobin = false ;
497
563
} else {
498
564
// Fall back to round robin if all values are non-positives
565
+ usesRoundRobin = true ;
499
566
unscaledMeanWeight = 1 ;
500
567
unscaledMaxWeight = 1 ;
501
568
}
@@ -521,7 +588,14 @@ static final class StaticStrideScheduler {
521
588
this .sequence = sequence ;
522
589
}
523
590
524
- /** Returns the next sequence number and atomically increases sequence with wraparound. */
591
+ // Without properly weighted channels, we do plain vanilla round_robin.
592
+ boolean usesRoundRobin () {
593
+ return usesRoundRobin ;
594
+ }
595
+
596
+ /**
597
+ * Returns the next sequence number and atomically increases sequence with wraparound.
598
+ */
525
599
private long nextSequence () {
526
600
return Integer .toUnsignedLong (sequence .getAndIncrement ());
527
601
}
0 commit comments