1
1
/*
2
- * Copyright 2016-2018 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 ;
@@ -101,7 +103,9 @@ public class DirectMessageListenerContainer extends AbstractMessageListenerConta
101
103
102
104
protected final List <SimpleConsumer > consumers = new LinkedList <>(); // NOSONAR
103
105
104
- private final List <SimpleConsumer > consumersToRestart = new LinkedList <>();
106
+ private final Set <SimpleConsumer > consumersToRestart = new LinkedHashSet <>();
107
+
108
+ private final Set <String > removedQueues = ConcurrentHashMap .newKeySet ();
105
109
106
110
private final MultiValueMap <String , SimpleConsumer > consumersByQueue = new LinkedMultiValueMap <>();
107
111
@@ -241,6 +245,7 @@ public void addQueueNames(String... queueNames) {
241
245
Assert .notNull (queueNames , "'queueNames' cannot be null" );
242
246
Assert .noNullElements (queueNames , "'queueNames' cannot contain null elements" );
243
247
try {
248
+ Arrays .stream (queueNames ).forEach (this .removedQueues ::remove );
244
249
addQueues (Arrays .stream (queueNames ));
245
250
}
246
251
catch (AmqpIOException e ) {
@@ -254,6 +259,9 @@ public void addQueues(Queue... queues) {
254
259
Assert .notNull (queues , "'queues' cannot be null" );
255
260
Assert .noNullElements (queues , "'queues' cannot contain null elements" );
256
261
try {
262
+ Arrays .stream (queues )
263
+ .map (q -> q .getActualName ())
264
+ .forEach (this .removedQueues ::remove );
257
265
addQueues (Arrays .stream (queues ).map (Queue ::getName ));
258
266
}
259
267
catch (AmqpIOException e ) {
@@ -296,7 +304,10 @@ private void removeQueues(Stream<String> queueNames) {
296
304
if (isRunning ()) {
297
305
synchronized (this .consumersMonitor ) {
298
306
checkStartState ();
299
- queueNames .map (this .consumersByQueue ::remove )
307
+ queueNames .map (queue -> {
308
+ this .removedQueues .add (queue );
309
+ return this .consumersByQueue .remove (queue );
310
+ })
300
311
.filter (Objects ::nonNull )
301
312
.flatMap (Collection ::stream )
302
313
.forEach (this ::cancelConsumer );
@@ -311,7 +322,21 @@ private void adjustConsumers(int newCount) {
311
322
for (String queue : getQueueNames ()) {
312
323
while (this .consumersByQueue .get (queue ) == null
313
324
|| this .consumersByQueue .get (queue ).size () < newCount ) { // NOSONAR never null
314
- doConsumeFromQueue (queue );
325
+ List <SimpleConsumer > cBQ = this .consumersByQueue .get (queue );
326
+ int index = 0 ;
327
+ if (cBQ != null ) {
328
+ // find a gap or set the index to the end
329
+ List <Integer > indices = cBQ .stream ()
330
+ .map (cons -> cons .getIndex ())
331
+ .sorted ()
332
+ .collect (Collectors .toList ());
333
+ for (index = 0 ; index < indices .size (); index ++) {
334
+ if (index < indices .get (index )) {
335
+ break ;
336
+ }
337
+ }
338
+ }
339
+ doConsumeFromQueue (queue , index );
315
340
}
316
341
List <SimpleConsumer > consumerList = this .consumersByQueue .get (queue );
317
342
if (consumerList != null && consumerList .size () > newCount ) {
@@ -428,20 +453,19 @@ private void startMonitor(long idleEventInterval, final Map<String, Queue> names
428
453
checkConsumers (now );
429
454
if (this .lastRestartAttempt + getFailedDeclarationRetryInterval () < now ) {
430
455
synchronized (this .consumersMonitor ) {
431
- List <SimpleConsumer > restartableConsumers = new ArrayList <>(this .consumersToRestart );
432
- this .consumersToRestart .clear ();
433
456
if (this .started ) {
457
+ List <SimpleConsumer > restartableConsumers = new ArrayList <>(this .consumersToRestart );
458
+ this .consumersToRestart .clear ();
434
459
if (restartableConsumers .size () > 0 ) {
435
460
doRedeclareElementsIfNecessary ();
436
461
}
437
462
Iterator <SimpleConsumer > iterator = restartableConsumers .iterator ();
438
463
while (iterator .hasNext ()) {
439
464
SimpleConsumer consumer = iterator .next ();
440
465
iterator .remove ();
441
- if (!DirectMessageListenerContainer .this .consumersByQueue
442
- .containsKey (consumer .getQueue ())) {
466
+ if (DirectMessageListenerContainer .this .removedQueues .contains (consumer .getQueue ())) {
443
467
if (this .logger .isDebugEnabled ()) {
444
- this .logger .debug ("Skipping restart of consumer " + consumer );
468
+ this .logger .debug ("Skipping restart of consumer, queue removed " + consumer );
445
469
}
446
470
continue ;
447
471
}
@@ -514,11 +538,11 @@ private boolean restartConsumer(final Map<String, Queue> namesToQueues, List<Sim
514
538
if (StringUtils .hasText (actualName )) {
515
539
namesToQueues .remove (consumer .getQueue ());
516
540
namesToQueues .put (actualName , queue );
517
- consumer = new SimpleConsumer (null , null , actualName );
541
+ consumer = new SimpleConsumer (null , null , actualName , consumer . getIndex () );
518
542
}
519
543
}
520
544
try {
521
- doConsumeFromQueue (consumer .getQueue ());
545
+ doConsumeFromQueue (consumer .getQueue (), consumer . getIndex () );
522
546
return true ;
523
547
}
524
548
catch (AmqpConnectException | AmqpIOException e ) {
@@ -644,12 +668,12 @@ private void consumeFromQueue(String queue) {
644
668
// Possible race with setConsumersPerQueue and the task launched by start()
645
669
if (CollectionUtils .isEmpty (list )) {
646
670
for (int i = 0 ; i < this .consumersPerQueue ; i ++) {
647
- doConsumeFromQueue (queue );
671
+ doConsumeFromQueue (queue , i );
648
672
}
649
673
}
650
674
}
651
675
652
- private void doConsumeFromQueue (String queue ) {
676
+ private void doConsumeFromQueue (String queue , int index ) {
653
677
if (!isActive ()) {
654
678
if (this .logger .isDebugEnabled ()) {
655
679
this .logger .debug ("Consume from queue " + queue + " ignore, container stopping" );
@@ -666,7 +690,7 @@ private void doConsumeFromQueue(String queue) {
666
690
}
667
691
catch (Exception e ) {
668
692
publishConsumerFailedEvent (e .getMessage (), false , e );
669
- addConsumerToRestart (new SimpleConsumer (null , null , queue ));
693
+ addConsumerToRestart (new SimpleConsumer (null , null , queue , index ));
670
694
throw e instanceof AmqpConnectException // NOSONAR exception type check
671
695
? (AmqpConnectException ) e
672
696
: new AmqpConnectException (e );
@@ -676,7 +700,7 @@ private void doConsumeFromQueue(String queue) {
676
700
SimpleResourceHolder .pop (getRoutingConnectionFactory ()); // NOSONAR never null here
677
701
}
678
702
}
679
- SimpleConsumer consumer = consume (queue , connection );
703
+ SimpleConsumer consumer = consume (queue , index , connection );
680
704
synchronized (this .consumersMonitor ) {
681
705
if (consumer != null ) {
682
706
this .cancellationLock .add (consumer );
@@ -693,13 +717,13 @@ private void doConsumeFromQueue(String queue) {
693
717
}
694
718
695
719
@ Nullable
696
- private SimpleConsumer consume (String queue , Connection connection ) {
720
+ private SimpleConsumer consume (String queue , int index , Connection connection ) {
697
721
Channel channel = null ;
698
722
SimpleConsumer consumer = null ;
699
723
try {
700
724
channel = connection .createChannel (isChannelTransacted ());
701
725
channel .basicQos (getPrefetchCount ());
702
- consumer = new SimpleConsumer (connection , channel , queue );
726
+ consumer = new SimpleConsumer (connection , channel , queue , index );
703
727
channel .queueDeclarePassive (queue );
704
728
consumer .consumerTag = channel .basicConsume (queue , getAcknowledgeMode ().isAutoAck (),
705
729
(getConsumerTagStrategy () != null
@@ -713,13 +737,14 @@ private SimpleConsumer consume(String queue, Connection connection) {
713
737
RabbitUtils .closeChannel (channel );
714
738
RabbitUtils .closeConnection (connection );
715
739
716
- consumer = handleConsumeException (queue , consumer , e );
740
+ consumer = handleConsumeException (queue , index , consumer , e );
717
741
}
718
742
return consumer ;
719
743
}
720
744
721
745
@ Nullable
722
- private SimpleConsumer handleConsumeException (String queue , SimpleConsumer consumerArg , Exception e ) {
746
+ private SimpleConsumer handleConsumeException (String queue , int index , @ Nullable SimpleConsumer consumerArg ,
747
+ Exception e ) {
723
748
724
749
SimpleConsumer consumer = consumerArg ;
725
750
if (e .getCause () instanceof ShutdownSignalException
@@ -730,6 +755,7 @@ private SimpleConsumer handleConsumeException(String queue, SimpleConsumer consu
730
755
}
731
756
else if (e .getCause () instanceof ShutdownSignalException
732
757
&& RabbitUtils .isPassiveDeclarationChannelClose ((ShutdownSignalException ) e .getCause ())) {
758
+ publishMissingQueueEvent (queue );
733
759
this .logger .error ("Queue not present, scheduling consumer "
734
760
+ (consumer == null ? "for queue " + queue : consumer ) + " for restart" , e );
735
761
}
@@ -739,7 +765,7 @@ else if (this.logger.isWarnEnabled()) {
739
765
}
740
766
741
767
if (consumer == null ) {
742
- addConsumerToRestart (new SimpleConsumer (null , null , queue ));
768
+ addConsumerToRestart (new SimpleConsumer (null , null , queue , index ));
743
769
}
744
770
else {
745
771
addConsumerToRestart (consumer );
@@ -833,11 +859,9 @@ private void cancelConsumer(SimpleConsumer consumer) {
833
859
}
834
860
835
861
private void addConsumerToRestart (SimpleConsumer consumer ) {
836
- if (this .started ) {
837
- this .consumersToRestart .add (consumer );
838
- if (this .logger .isTraceEnabled ()) {
839
- this .logger .trace ("Consumers to restart now: " + this .consumersToRestart );
840
- }
862
+ this .consumersToRestart .add (consumer );
863
+ if (this .logger .isTraceEnabled ()) {
864
+ this .logger .trace ("Consumers to restart now: " + this .consumersToRestart );
841
865
}
842
866
}
843
867
@@ -860,6 +884,8 @@ final class SimpleConsumer extends DefaultConsumer {
860
884
861
885
private final String queue ;
862
886
887
+ private final int index ;
888
+
863
889
private final boolean ackRequired ;
864
890
865
891
private final ConnectionFactory connectionFactory = getConnectionFactory ();
@@ -894,10 +920,11 @@ final class SimpleConsumer extends DefaultConsumer {
894
920
895
921
private volatile boolean ackFailed ;
896
922
897
- SimpleConsumer (Connection connection , Channel channel , String queue ) {
923
+ SimpleConsumer (@ Nullable Connection connection , @ Nullable Channel channel , String queue , int index ) {
898
924
super (channel );
899
925
this .connection = connection ;
900
926
this .queue = queue ;
927
+ this .index = index ;
901
928
this .ackRequired = !getAcknowledgeMode ().isAutoAck () && !getAcknowledgeMode ().isManual ();
902
929
if (channel instanceof ChannelProxy ) {
903
930
this .targetChannel = ((ChannelProxy ) channel ).getTargetChannel ();
@@ -907,10 +934,14 @@ final class SimpleConsumer extends DefaultConsumer {
907
934
}
908
935
}
909
936
910
- private String getQueue () {
937
+ String getQueue () {
911
938
return this .queue ;
912
939
}
913
940
941
+ int getIndex () {
942
+ return this .index ;
943
+ }
944
+
914
945
@ Override
915
946
public String getConsumerTag () {
916
947
return this .consumerTag ;
@@ -1203,9 +1234,53 @@ private void finalizeConsumer() {
1203
1234
consumerRemoved (this );
1204
1235
}
1205
1236
1237
+ @ Override
1238
+ public int hashCode () {
1239
+ final int prime = 31 ;
1240
+ int result = 1 ;
1241
+ result = prime * result + getEnclosingInstance ().hashCode ();
1242
+ result = prime * result + this .index ;
1243
+ result = prime * result + ((this .queue == null ) ? 0 : this .queue .hashCode ());
1244
+ return result ;
1245
+ }
1246
+
1247
+ @ Override
1248
+ public boolean equals (Object obj ) {
1249
+ if (this == obj ) {
1250
+ return true ;
1251
+ }
1252
+ if (obj == null ) {
1253
+ return false ;
1254
+ }
1255
+ if (getClass () != obj .getClass ()) {
1256
+ return false ;
1257
+ }
1258
+ SimpleConsumer other = (SimpleConsumer ) obj ;
1259
+ if (!getEnclosingInstance ().equals (other .getEnclosingInstance ())) {
1260
+ return false ;
1261
+ }
1262
+ if (this .index != other .index ) {
1263
+ return false ;
1264
+ }
1265
+ if (this .queue == null ) {
1266
+ if (other .queue != null ) {
1267
+ return false ;
1268
+ }
1269
+ }
1270
+ else if (!this .queue .equals (other .queue )) {
1271
+ return false ;
1272
+ }
1273
+ return true ;
1274
+ }
1275
+
1276
+ private DirectMessageListenerContainer getEnclosingInstance () {
1277
+ return DirectMessageListenerContainer .this ;
1278
+ }
1279
+
1206
1280
@ Override
1207
1281
public String toString () {
1208
- return "SimpleConsumer [queue=" + this .queue + ", consumerTag=" + this .consumerTag
1282
+ return "SimpleConsumer [queue=" + this .queue + ", index=" + this .index
1283
+ + ", consumerTag=" + this .consumerTag
1209
1284
+ " identity=" + ObjectUtils .getIdentityHexString (this ) + "]" ;
1210
1285
}
1211
1286
0 commit comments