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,13 +719,13 @@ 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 {
702
726
channel = connection .createChannel (isChannelTransacted ());
703
727
channel .basicQos (getPrefetchCount ());
704
- consumer = new SimpleConsumer (connection , channel , queue );
728
+ consumer = new SimpleConsumer (connection , channel , queue , index );
705
729
channel .queueDeclarePassive (queue );
706
730
consumer .consumerTag = channel .basicConsume (queue , getAcknowledgeMode ().isAutoAck (),
707
731
(getConsumerTagStrategy () != null
@@ -715,13 +739,14 @@ private SimpleConsumer consume(String queue, Connection connection) {
715
739
RabbitUtils .closeChannel (channel );
716
740
RabbitUtils .closeConnection (connection );
717
741
718
- consumer = handleConsumeException (queue , consumer , e );
742
+ consumer = handleConsumeException (queue , index , consumer , e );
719
743
}
720
744
return consumer ;
721
745
}
722
746
723
747
@ Nullable
724
- private SimpleConsumer handleConsumeException (String queue , @ Nullable SimpleConsumer consumerArg , Exception e ) {
748
+ private SimpleConsumer handleConsumeException (String queue , int index , @ Nullable SimpleConsumer consumerArg ,
749
+ Exception e ) {
725
750
726
751
SimpleConsumer consumer = consumerArg ;
727
752
if (e .getCause () instanceof ShutdownSignalException
@@ -732,6 +757,7 @@ private SimpleConsumer handleConsumeException(String queue, @Nullable SimpleCons
732
757
}
733
758
else if (e .getCause () instanceof ShutdownSignalException
734
759
&& RabbitUtils .isPassiveDeclarationChannelClose ((ShutdownSignalException ) e .getCause ())) {
760
+ publishMissingQueueEvent (queue );
735
761
this .logger .error ("Queue not present, scheduling consumer "
736
762
+ (consumer == null ? "for queue " + queue : consumer ) + " for restart" , e );
737
763
}
@@ -741,7 +767,7 @@ else if (this.logger.isWarnEnabled()) {
741
767
}
742
768
743
769
if (consumer == null ) {
744
- addConsumerToRestart (new SimpleConsumer (null , null , queue ));
770
+ addConsumerToRestart (new SimpleConsumer (null , null , queue , index ));
745
771
}
746
772
else {
747
773
addConsumerToRestart (consumer );
@@ -836,11 +862,9 @@ private void cancelConsumer(SimpleConsumer consumer) {
836
862
}
837
863
838
864
private void addConsumerToRestart (SimpleConsumer consumer ) {
839
- if (this .started ) {
840
- this .consumersToRestart .add (consumer );
841
- if (this .logger .isTraceEnabled ()) {
842
- this .logger .trace ("Consumers to restart now: " + this .consumersToRestart );
843
- }
865
+ this .consumersToRestart .add (consumer );
866
+ if (this .logger .isTraceEnabled ()) {
867
+ this .logger .trace ("Consumers to restart now: " + this .consumersToRestart );
844
868
}
845
869
}
846
870
@@ -863,6 +887,8 @@ final class SimpleConsumer extends DefaultConsumer {
863
887
864
888
private final String queue ;
865
889
890
+ private final int index ;
891
+
866
892
private final boolean ackRequired ;
867
893
868
894
private final ConnectionFactory connectionFactory = getConnectionFactory ();
@@ -895,10 +921,11 @@ final class SimpleConsumer extends DefaultConsumer {
895
921
896
922
private volatile boolean ackFailed ;
897
923
898
- SimpleConsumer (@ Nullable Connection connection , @ Nullable Channel channel , String queue ) {
924
+ SimpleConsumer (@ Nullable Connection connection , @ Nullable Channel channel , String queue , int index ) {
899
925
super (channel );
900
926
this .connection = connection ;
901
927
this .queue = queue ;
928
+ this .index = index ;
902
929
this .ackRequired = !getAcknowledgeMode ().isAutoAck () && !getAcknowledgeMode ().isManual ();
903
930
if (channel instanceof ChannelProxy ) {
904
931
this .targetChannel = ((ChannelProxy ) channel ).getTargetChannel ();
@@ -908,10 +935,14 @@ final class SimpleConsumer extends DefaultConsumer {
908
935
}
909
936
}
910
937
911
- private String getQueue () {
938
+ String getQueue () {
912
939
return this .queue ;
913
940
}
914
941
942
+ int getIndex () {
943
+ return this .index ;
944
+ }
945
+
915
946
@ Override
916
947
public String getConsumerTag () {
917
948
return this .consumerTag ;
@@ -1212,9 +1243,53 @@ private void finalizeConsumer() {
1212
1243
consumerRemoved (this );
1213
1244
}
1214
1245
1246
+ @ Override
1247
+ public int hashCode () {
1248
+ final int prime = 31 ;
1249
+ int result = 1 ;
1250
+ result = prime * result + getEnclosingInstance ().hashCode ();
1251
+ result = prime * result + this .index ;
1252
+ result = prime * result + ((this .queue == null ) ? 0 : this .queue .hashCode ());
1253
+ return result ;
1254
+ }
1255
+
1256
+ @ Override
1257
+ public boolean equals (Object obj ) {
1258
+ if (this == obj ) {
1259
+ return true ;
1260
+ }
1261
+ if (obj == null ) {
1262
+ return false ;
1263
+ }
1264
+ if (getClass () != obj .getClass ()) {
1265
+ return false ;
1266
+ }
1267
+ SimpleConsumer other = (SimpleConsumer ) obj ;
1268
+ if (!getEnclosingInstance ().equals (other .getEnclosingInstance ())) {
1269
+ return false ;
1270
+ }
1271
+ if (this .index != other .index ) {
1272
+ return false ;
1273
+ }
1274
+ if (this .queue == null ) {
1275
+ if (other .queue != null ) {
1276
+ return false ;
1277
+ }
1278
+ }
1279
+ else if (!this .queue .equals (other .queue )) {
1280
+ return false ;
1281
+ }
1282
+ return true ;
1283
+ }
1284
+
1285
+ private DirectMessageListenerContainer getEnclosingInstance () {
1286
+ return DirectMessageListenerContainer .this ;
1287
+ }
1288
+
1215
1289
@ Override
1216
1290
public String toString () {
1217
- return "SimpleConsumer [queue=" + this .queue + ", consumerTag=" + this .consumerTag
1291
+ return "SimpleConsumer [queue=" + this .queue + ", index=" + this .index
1292
+ + ", consumerTag=" + this .consumerTag
1218
1293
+ " identity=" + ObjectUtils .getIdentityHexString (this ) + "]" ;
1219
1294
}
1220
1295
0 commit comments