1
1
/*
2
- * Copyright 2016-2020 the original author or authors.
2
+ * Copyright 2016-2021 the original author or authors.
3
3
*
4
4
* Licensed under the Apache License, Version 2.0 (the "License");
5
5
* you may not use this file except in compliance with the License.
24
24
import java .util .Collections ;
25
25
import java .util .Date ;
26
26
import java .util .Iterator ;
27
+ import java .util .LinkedHashSet ;
27
28
import java .util .LinkedList ;
28
29
import java .util .List ;
29
30
import java .util .Map ;
30
31
import java .util .Objects ;
31
32
import java .util .Properties ;
32
33
import java .util .Set ;
34
+ import java .util .concurrent .ConcurrentHashMap ;
33
35
import java .util .concurrent .CountDownLatch ;
34
36
import java .util .concurrent .ScheduledFuture ;
35
37
import java .util .concurrent .TimeUnit ;
@@ -103,7 +105,9 @@ public class DirectMessageListenerContainer extends AbstractMessageListenerConta
103
105
104
106
protected final List <SimpleConsumer > consumers = new LinkedList <>(); // NOSONAR
105
107
106
- private final List <SimpleConsumer > consumersToRestart = new LinkedList <>();
108
+ private final Set <SimpleConsumer > consumersToRestart = new LinkedHashSet <>();
109
+
110
+ private final Set <String > removedQueues = ConcurrentHashMap .newKeySet ();
107
111
108
112
private final MultiValueMap <String , SimpleConsumer > consumersByQueue = new LinkedMultiValueMap <>();
109
113
@@ -243,6 +247,7 @@ public void addQueueNames(String... queueNames) {
243
247
Assert .notNull (queueNames , "'queueNames' cannot be null" );
244
248
Assert .noNullElements (queueNames , "'queueNames' cannot contain null elements" );
245
249
try {
250
+ Arrays .stream (queueNames ).forEach (this .removedQueues ::remove );
246
251
addQueues (Arrays .stream (queueNames ));
247
252
}
248
253
catch (AmqpIOException e ) {
@@ -256,6 +261,9 @@ public void addQueues(Queue... queues) {
256
261
Assert .notNull (queues , "'queues' cannot be null" );
257
262
Assert .noNullElements (queues , "'queues' cannot contain null elements" );
258
263
try {
264
+ Arrays .stream (queues )
265
+ .map (q -> q .getActualName ())
266
+ .forEach (this .removedQueues ::remove );
259
267
addQueues (Arrays .stream (queues ).map (Queue ::getName ));
260
268
}
261
269
catch (AmqpIOException e ) {
@@ -298,7 +306,10 @@ private void removeQueues(Stream<String> queueNames) {
298
306
if (isRunning ()) {
299
307
synchronized (this .consumersMonitor ) {
300
308
checkStartState ();
301
- queueNames .map (this .consumersByQueue ::remove )
309
+ queueNames .map (queue -> {
310
+ this .removedQueues .add (queue );
311
+ return this .consumersByQueue .remove (queue );
312
+ })
302
313
.filter (Objects ::nonNull )
303
314
.flatMap (Collection ::stream )
304
315
.forEach (this ::cancelConsumer );
@@ -313,7 +324,21 @@ private void adjustConsumers(int newCount) {
313
324
for (String queue : getQueueNames ()) {
314
325
while (this .consumersByQueue .get (queue ) == null
315
326
|| this .consumersByQueue .get (queue ).size () < newCount ) { // NOSONAR never null
316
- doConsumeFromQueue (queue );
327
+ List <SimpleConsumer > cBQ = this .consumersByQueue .get (queue );
328
+ int index = 0 ;
329
+ if (cBQ != null ) {
330
+ // find a gap or set the index to the end
331
+ List <Integer > indices = cBQ .stream ()
332
+ .map (cons -> cons .getIndex ())
333
+ .sorted ()
334
+ .collect (Collectors .toList ());
335
+ for (index = 0 ; index < indices .size (); index ++) {
336
+ if (index < indices .get (index )) {
337
+ break ;
338
+ }
339
+ }
340
+ }
341
+ doConsumeFromQueue (queue , index );
317
342
}
318
343
List <SimpleConsumer > consumerList = this .consumersByQueue .get (queue );
319
344
if (consumerList != null && consumerList .size () > newCount ) {
@@ -430,20 +455,19 @@ private void startMonitor(long idleEventInterval, final Map<String, Queue> names
430
455
checkConsumers (now );
431
456
if (this .lastRestartAttempt + getFailedDeclarationRetryInterval () < now ) {
432
457
synchronized (this .consumersMonitor ) {
433
- List <SimpleConsumer > restartableConsumers = new ArrayList <>(this .consumersToRestart );
434
- this .consumersToRestart .clear ();
435
458
if (this .started ) {
459
+ List <SimpleConsumer > restartableConsumers = new ArrayList <>(this .consumersToRestart );
460
+ this .consumersToRestart .clear ();
436
461
if (restartableConsumers .size () > 0 ) {
437
462
doRedeclareElementsIfNecessary ();
438
463
}
439
464
Iterator <SimpleConsumer > iterator = restartableConsumers .iterator ();
440
465
while (iterator .hasNext ()) {
441
466
SimpleConsumer consumer = iterator .next ();
442
467
iterator .remove ();
443
- if (!DirectMessageListenerContainer .this .consumersByQueue
444
- .containsKey (consumer .getQueue ())) {
468
+ if (DirectMessageListenerContainer .this .removedQueues .contains (consumer .getQueue ())) {
445
469
if (this .logger .isDebugEnabled ()) {
446
- this .logger .debug ("Skipping restart of consumer " + consumer );
470
+ this .logger .debug ("Skipping restart of consumer, queue removed " + consumer );
447
471
}
448
472
continue ;
449
473
}
@@ -516,11 +540,11 @@ private boolean restartConsumer(final Map<String, Queue> namesToQueues, List<Sim
516
540
if (StringUtils .hasText (actualName )) {
517
541
namesToQueues .remove (consumer .getQueue ());
518
542
namesToQueues .put (actualName , queue );
519
- consumer = new SimpleConsumer (null , null , actualName );
543
+ consumer = new SimpleConsumer (null , null , actualName , consumer . getIndex () );
520
544
}
521
545
}
522
546
try {
523
- doConsumeFromQueue (consumer .getQueue ());
547
+ doConsumeFromQueue (consumer .getQueue (), consumer . getIndex () );
524
548
return true ;
525
549
}
526
550
catch (AmqpConnectException | AmqpIOException e ) {
@@ -646,12 +670,12 @@ private void consumeFromQueue(String queue) {
646
670
// Possible race with setConsumersPerQueue and the task launched by start()
647
671
if (CollectionUtils .isEmpty (list )) {
648
672
for (int i = 0 ; i < this .consumersPerQueue ; i ++) {
649
- doConsumeFromQueue (queue );
673
+ doConsumeFromQueue (queue , i );
650
674
}
651
675
}
652
676
}
653
677
654
- private void doConsumeFromQueue (String queue ) {
678
+ private void doConsumeFromQueue (String queue , int index ) {
655
679
if (!isActive ()) {
656
680
if (this .logger .isDebugEnabled ()) {
657
681
this .logger .debug ("Consume from queue " + queue + " ignore, container stopping" );
@@ -668,7 +692,7 @@ private void doConsumeFromQueue(String queue) {
668
692
}
669
693
catch (Exception e ) {
670
694
publishConsumerFailedEvent (e .getMessage (), false , e );
671
- addConsumerToRestart (new SimpleConsumer (null , null , queue ));
695
+ addConsumerToRestart (new SimpleConsumer (null , null , queue , index ));
672
696
throw e instanceof AmqpConnectException // NOSONAR exception type check
673
697
? (AmqpConnectException ) e
674
698
: new AmqpConnectException (e );
@@ -678,7 +702,7 @@ private void doConsumeFromQueue(String queue) {
678
702
SimpleResourceHolder .pop (getRoutingConnectionFactory ()); // NOSONAR never null here
679
703
}
680
704
}
681
- SimpleConsumer consumer = consume (queue , connection );
705
+ SimpleConsumer consumer = consume (queue , index , connection );
682
706
synchronized (this .consumersMonitor ) {
683
707
if (consumer != null ) {
684
708
this .cancellationLock .add (consumer );
@@ -695,7 +719,7 @@ private void doConsumeFromQueue(String queue) {
695
719
}
696
720
697
721
@ Nullable
698
- private SimpleConsumer consume (String queue , Connection connection ) {
722
+ private SimpleConsumer consume (String queue , int index , Connection connection ) {
699
723
Channel channel = null ;
700
724
SimpleConsumer consumer = null ;
701
725
try {
@@ -709,7 +733,7 @@ private SimpleConsumer consume(String queue, Connection connection) {
709
733
}
710
734
channel = connection .createChannel (isChannelTransacted ());
711
735
channel .basicQos (getPrefetchCount ());
712
- consumer = new SimpleConsumer (connection , channel , queue );
736
+ consumer = new SimpleConsumer (connection , channel , queue , index );
713
737
channel .queueDeclarePassive (queue );
714
738
consumer .consumerTag = channel .basicConsume (queue , getAcknowledgeMode ().isAutoAck (),
715
739
(getConsumerTagStrategy () != null
@@ -723,13 +747,14 @@ private SimpleConsumer consume(String queue, Connection connection) {
723
747
RabbitUtils .closeChannel (channel );
724
748
RabbitUtils .closeConnection (connection );
725
749
726
- consumer = handleConsumeException (queue , consumer , e );
750
+ consumer = handleConsumeException (queue , index , consumer , e );
727
751
}
728
752
return consumer ;
729
753
}
730
754
731
755
@ Nullable
732
- private SimpleConsumer handleConsumeException (String queue , @ Nullable SimpleConsumer consumerArg , Exception e ) {
756
+ private SimpleConsumer handleConsumeException (String queue , int index , @ Nullable SimpleConsumer consumerArg ,
757
+ Exception e ) {
733
758
734
759
SimpleConsumer consumer = consumerArg ;
735
760
if (e .getCause () instanceof ShutdownSignalException
@@ -740,6 +765,7 @@ private SimpleConsumer handleConsumeException(String queue, @Nullable SimpleCons
740
765
}
741
766
else if (e .getCause () instanceof ShutdownSignalException
742
767
&& RabbitUtils .isPassiveDeclarationChannelClose ((ShutdownSignalException ) e .getCause ())) {
768
+ publishMissingQueueEvent (queue );
743
769
this .logger .error ("Queue not present, scheduling consumer "
744
770
+ (consumer == null ? "for queue " + queue : consumer ) + " for restart" , e );
745
771
}
@@ -749,7 +775,7 @@ else if (this.logger.isWarnEnabled()) {
749
775
}
750
776
751
777
if (consumer == null ) {
752
- addConsumerToRestart (new SimpleConsumer (null , null , queue ));
778
+ addConsumerToRestart (new SimpleConsumer (null , null , queue , index ));
753
779
}
754
780
else {
755
781
addConsumerToRestart (consumer );
@@ -844,11 +870,9 @@ private void cancelConsumer(SimpleConsumer consumer) {
844
870
}
845
871
846
872
private void addConsumerToRestart (SimpleConsumer consumer ) {
847
- if (this .started ) {
848
- this .consumersToRestart .add (consumer );
849
- if (this .logger .isTraceEnabled ()) {
850
- this .logger .trace ("Consumers to restart now: " + this .consumersToRestart );
851
- }
873
+ this .consumersToRestart .add (consumer );
874
+ if (this .logger .isTraceEnabled ()) {
875
+ this .logger .trace ("Consumers to restart now: " + this .consumersToRestart );
852
876
}
853
877
}
854
878
@@ -871,6 +895,8 @@ final class SimpleConsumer extends DefaultConsumer {
871
895
872
896
private final String queue ;
873
897
898
+ private final int index ;
899
+
874
900
private final boolean ackRequired ;
875
901
876
902
private final ConnectionFactory connectionFactory = getConnectionFactory ();
@@ -903,10 +929,11 @@ final class SimpleConsumer extends DefaultConsumer {
903
929
904
930
private volatile boolean ackFailed ;
905
931
906
- SimpleConsumer (@ Nullable Connection connection , @ Nullable Channel channel , String queue ) {
932
+ SimpleConsumer (@ Nullable Connection connection , @ Nullable Channel channel , String queue , int index ) {
907
933
super (channel );
908
934
this .connection = connection ;
909
935
this .queue = queue ;
936
+ this .index = index ;
910
937
this .ackRequired = !getAcknowledgeMode ().isAutoAck () && !getAcknowledgeMode ().isManual ();
911
938
if (channel instanceof ChannelProxy ) {
912
939
this .targetChannel = ((ChannelProxy ) channel ).getTargetChannel ();
@@ -916,10 +943,14 @@ final class SimpleConsumer extends DefaultConsumer {
916
943
}
917
944
}
918
945
919
- private String getQueue () {
946
+ String getQueue () {
920
947
return this .queue ;
921
948
}
922
949
950
+ int getIndex () {
951
+ return this .index ;
952
+ }
953
+
923
954
@ Override
924
955
public String getConsumerTag () {
925
956
return this .consumerTag ;
@@ -1220,9 +1251,53 @@ private void finalizeConsumer() {
1220
1251
consumerRemoved (this );
1221
1252
}
1222
1253
1254
+ @ Override
1255
+ public int hashCode () {
1256
+ final int prime = 31 ;
1257
+ int result = 1 ;
1258
+ result = prime * result + getEnclosingInstance ().hashCode ();
1259
+ result = prime * result + this .index ;
1260
+ result = prime * result + ((this .queue == null ) ? 0 : this .queue .hashCode ());
1261
+ return result ;
1262
+ }
1263
+
1264
+ @ Override
1265
+ public boolean equals (Object obj ) {
1266
+ if (this == obj ) {
1267
+ return true ;
1268
+ }
1269
+ if (obj == null ) {
1270
+ return false ;
1271
+ }
1272
+ if (getClass () != obj .getClass ()) {
1273
+ return false ;
1274
+ }
1275
+ SimpleConsumer other = (SimpleConsumer ) obj ;
1276
+ if (!getEnclosingInstance ().equals (other .getEnclosingInstance ())) {
1277
+ return false ;
1278
+ }
1279
+ if (this .index != other .index ) {
1280
+ return false ;
1281
+ }
1282
+ if (this .queue == null ) {
1283
+ if (other .queue != null ) {
1284
+ return false ;
1285
+ }
1286
+ }
1287
+ else if (!this .queue .equals (other .queue )) {
1288
+ return false ;
1289
+ }
1290
+ return true ;
1291
+ }
1292
+
1293
+ private DirectMessageListenerContainer getEnclosingInstance () {
1294
+ return DirectMessageListenerContainer .this ;
1295
+ }
1296
+
1223
1297
@ Override
1224
1298
public String toString () {
1225
- return "SimpleConsumer [queue=" + this .queue + ", consumerTag=" + this .consumerTag
1299
+ return "SimpleConsumer [queue=" + this .queue + ", index=" + this .index
1300
+ + ", consumerTag=" + this .consumerTag
1226
1301
+ " identity=" + ObjectUtils .getIdentityHexString (this ) + "]" ;
1227
1302
}
1228
1303
0 commit comments