16
16
17
17
import static com .rabbitmq .stream .impl .Utils .*;
18
18
import static java .lang .String .format ;
19
+ import static java .util .stream .Collectors .toList ;
19
20
20
21
import com .rabbitmq .stream .*;
21
22
import com .rabbitmq .stream .Consumer ;
35
36
import java .util .Map .Entry ;
36
37
import java .util .NavigableSet ;
37
38
import java .util .Objects ;
38
- import java .util .Random ;
39
39
import java .util .Set ;
40
40
import java .util .concurrent .Callable ;
41
41
import java .util .concurrent .ConcurrentHashMap ;
53
53
import org .slf4j .Logger ;
54
54
import org .slf4j .LoggerFactory ;
55
55
56
- class ConsumersCoordinator {
56
+ final class ConsumersCoordinator implements AutoCloseable {
57
57
58
58
static final int MAX_SUBSCRIPTIONS_PER_CLIENT = 256 ;
59
59
static final int MAX_ATTEMPT_BEFORE_FALLING_BACK_TO_LEADER = 5 ;
@@ -62,7 +62,6 @@ class ConsumersCoordinator {
62
62
static final OffsetSpecification DEFAULT_OFFSET_SPECIFICATION = OffsetSpecification .next ();
63
63
64
64
private static final Logger LOGGER = LoggerFactory .getLogger (ConsumersCoordinator .class );
65
- private final Random random = new Random ();
66
65
private final StreamEnvironment environment ;
67
66
private final ClientFactory clientFactory ;
68
67
private final int maxConsumersByConnection ;
@@ -115,8 +114,8 @@ Runnable subscribe(
115
114
return lock (
116
115
this .coordinatorLock ,
117
116
() -> {
118
- List <Client . Broker > candidates = findBrokersForStream (stream , forceReplica );
119
- Client . Broker newNode = pickBroker (candidates );
117
+ List <BrokerWrapper > candidates = findCandidateNodes (stream , forceReplica );
118
+ Broker newNode = pickBroker (this . brokerPicker , candidates );
120
119
if (newNode == null ) {
121
120
throw new IllegalStateException ("No available node to subscribe to" );
122
121
}
@@ -161,7 +160,7 @@ Runnable subscribe(
161
160
162
161
private void addToManager (
163
162
Broker node ,
164
- List <Broker > candidates ,
163
+ List <BrokerWrapper > candidates ,
165
164
SubscriptionTracker tracker ,
166
165
OffsetSpecification offsetSpecification ,
167
166
boolean isInitialSubscription ) {
@@ -231,7 +230,7 @@ int managerCount() {
231
230
}
232
231
233
232
// package protected for testing
234
- List <Client . Broker > findBrokersForStream (String stream , boolean forceReplica ) {
233
+ List <BrokerWrapper > findCandidateNodes (String stream , boolean forceReplica ) {
235
234
LOGGER .debug (
236
235
"Candidate lookup to consumer from '{}', forcing replica? {}" , stream , forceReplica );
237
236
Map <String , Client .StreamMetadata > metadata =
@@ -254,12 +253,13 @@ List<Client.Broker> findBrokersForStream(String stream, boolean forceReplica) {
254
253
}
255
254
}
256
255
257
- List <Client .Broker > replicas = streamMetadata .getReplicas ();
258
- if ((replicas == null || replicas .isEmpty ()) && streamMetadata .getLeader () == null ) {
256
+ Broker leader = streamMetadata .getLeader ();
257
+ List <Broker > replicas = streamMetadata .getReplicas ();
258
+ if ((replicas == null || replicas .isEmpty ()) && leader == null ) {
259
259
throw new IllegalStateException ("No node available to consume from stream " + stream );
260
260
}
261
261
262
- List <Client . Broker > brokers ;
262
+ List <BrokerWrapper > brokers ;
263
263
if (replicas == null || replicas .isEmpty ()) {
264
264
if (forceReplica ) {
265
265
throw new IllegalStateException (
@@ -268,21 +268,26 @@ List<Client.Broker> findBrokersForStream(String stream, boolean forceReplica) {
268
268
+ "consuming from leader has been deactivated for this consumer" ,
269
269
stream ));
270
270
} else {
271
- brokers = Collections .singletonList (streamMetadata .getLeader ());
272
- LOGGER .debug (
273
- "Only leader node {} for consuming from {}" , streamMetadata .getLeader (), stream );
271
+ brokers = Collections .singletonList (new BrokerWrapper (leader , true ));
272
+ LOGGER .debug ("Only leader node {} for consuming from {}" , leader , stream );
274
273
}
275
274
} else {
276
275
LOGGER .debug ("Replicas for consuming from {}: {}" , stream , replicas );
277
- brokers = new ArrayList <>(replicas );
276
+ brokers =
277
+ replicas .stream ()
278
+ .map (b -> new BrokerWrapper (b , false ))
279
+ .collect (Collectors .toCollection (ArrayList ::new ));
280
+ if (!forceReplica && leader != null ) {
281
+ brokers .add (new BrokerWrapper (leader , true ));
282
+ }
278
283
}
279
284
280
285
LOGGER .debug ("Candidates to consume from {}: {}" , stream , brokers );
281
286
282
287
return brokers ;
283
288
}
284
289
285
- private Callable <List <Broker >> findBrokersForStream (String stream ) {
290
+ private Callable <List <BrokerWrapper >> findCandidateNodes (String stream ) {
286
291
AtomicInteger attemptNumber = new AtomicInteger ();
287
292
return () -> {
288
293
boolean mustUseReplica ;
@@ -294,20 +299,10 @@ private Callable<List<Broker>> findBrokersForStream(String stream) {
294
299
}
295
300
LOGGER .debug (
296
301
"Looking for broker(s) for stream {}, forcing replica {}" , stream , mustUseReplica );
297
- return findBrokersForStream (stream , mustUseReplica );
302
+ return findCandidateNodes (stream , mustUseReplica );
298
303
};
299
304
}
300
305
301
- private Client .Broker pickBroker (List <Client .Broker > brokers ) {
302
- if (brokers .isEmpty ()) {
303
- return null ;
304
- } else if (brokers .size () == 1 ) {
305
- return brokers .get (0 );
306
- } else {
307
- return brokers .get (random .nextInt (brokers .size ()));
308
- }
309
- }
310
-
311
306
public void close () {
312
307
Iterator <ClientSubscriptionsManager > iterator = this .managers .iterator ();
313
308
while (iterator .hasNext ()) {
@@ -584,7 +579,9 @@ private class ClientSubscriptionsManager implements Comparable<ClientSubscriptio
584
579
private final AtomicBoolean closed = new AtomicBoolean (false );
585
580
586
581
private ClientSubscriptionsManager (
587
- Broker targetNode , List <Broker > candidates , Client .ClientParameters clientParameters ) {
582
+ Broker targetNode ,
583
+ List <BrokerWrapper > candidates ,
584
+ Client .ClientParameters clientParameters ) {
588
585
this .id = managerIdSequence .getAndIncrement ();
589
586
this .trackerCount = 0 ;
590
587
AtomicReference <String > nameReference = new AtomicReference <>();
@@ -804,7 +801,7 @@ private ClientSubscriptionsManager(
804
801
.metadataListener (metadataListener )
805
802
.consumerUpdateListener (consumerUpdateListener ),
806
803
keyForNode (targetNode ),
807
- candidates );
804
+ candidates . stream (). map ( BrokerWrapper :: broker ). collect ( toList ()) );
808
805
this .client = clientFactory .client (clientFactoryContext );
809
806
this .node = brokerFromClient (this .client );
810
807
this .name = keyForNode (this .node );
@@ -834,15 +831,15 @@ private void assignConsumersToStream(
834
831
}
835
832
};
836
833
837
- AsyncRetry .asyncRetry (findBrokersForStream (stream ))
834
+ AsyncRetry .asyncRetry (findCandidateNodes (stream ))
838
835
.description ("Candidate lookup to consume from '%s'" , stream )
839
836
.scheduler (environment .scheduledExecutorService ())
840
837
.retry (ex -> !(ex instanceof StreamDoesNotExistException ))
841
838
.delayPolicy (delayPolicy )
842
839
.build ()
843
840
.thenAccept (
844
841
candidateNodes -> {
845
- List <Broker > candidates = candidateNodes ;
842
+ List <BrokerWrapper > candidates = candidateNodes ;
846
843
if (candidates == null ) {
847
844
LOGGER .debug ("No candidate nodes to consume from '{}'" , stream );
848
845
consumersClosingCallback .run ();
@@ -876,7 +873,8 @@ private List<SubscriptionTracker> createSubscriptionTrackerList() {
876
873
return newSubscriptions ;
877
874
}
878
875
879
- private void maybeRecoverSubscription (List <Broker > candidates , SubscriptionTracker tracker ) {
876
+ private void maybeRecoverSubscription (
877
+ List <BrokerWrapper > candidates , SubscriptionTracker tracker ) {
880
878
if (tracker .compareAndSet (SubscriptionState .ACTIVE , SubscriptionState .RECOVERING )) {
881
879
try {
882
880
recoverSubscription (candidates , tracker );
@@ -897,12 +895,12 @@ private void maybeRecoverSubscription(List<Broker> candidates, SubscriptionTrack
897
895
}
898
896
}
899
897
900
- private void recoverSubscription (List <Broker > candidates , SubscriptionTracker tracker ) {
898
+ private void recoverSubscription (List <BrokerWrapper > candidates , SubscriptionTracker tracker ) {
901
899
boolean reassignmentCompleted = false ;
902
900
while (!reassignmentCompleted ) {
903
901
try {
904
902
if (tracker .consumer .isOpen ()) {
905
- Broker broker = pickBroker (candidates );
903
+ Broker broker = pickBroker (brokerPicker , candidates );
906
904
LOGGER .debug ("Using {} to resume consuming from {}" , broker , tracker .stream );
907
905
synchronized (tracker .consumer ) {
908
906
if (tracker .consumer .isOpen ()) {
@@ -933,7 +931,7 @@ private void recoverSubscription(List<Broker> candidates, SubscriptionTracker tr
933
931
// maybe not a good candidate, let's refresh and retry for this one
934
932
candidates =
935
933
Utils .callAndMaybeRetry (
936
- findBrokersForStream (tracker .stream ),
934
+ findCandidateNodes (tracker .stream ),
937
935
ex -> !(ex instanceof StreamDoesNotExistException ),
938
936
recoveryBackOffDelayPolicy (),
939
937
"Candidate lookup to consume from '%s' (subscription recovery)" ,
@@ -1301,4 +1299,20 @@ static <T> int pickSlot(List<T> list, AtomicInteger sequence) {
1301
1299
}
1302
1300
return index ;
1303
1301
}
1302
+
1303
+ private static List <Broker > keepReplicasIfPossible (Collection <BrokerWrapper > brokers ) {
1304
+ if (brokers .size () > 1 ) {
1305
+ return brokers .stream ()
1306
+ .filter (w -> !w .isLeader ())
1307
+ .map (BrokerWrapper ::broker )
1308
+ .collect (toList ());
1309
+ } else {
1310
+ return brokers .stream ().map (BrokerWrapper ::broker ).collect (toList ());
1311
+ }
1312
+ }
1313
+
1314
+ static Broker pickBroker (
1315
+ Function <List <Broker >, Broker > picker , Collection <BrokerWrapper > candidates ) {
1316
+ return picker .apply (keepReplicasIfPossible (candidates ));
1317
+ }
1304
1318
}
0 commit comments