@@ -95,6 +95,8 @@ public class AutorecoveringConnection implements RecoverableConnection, NetworkC
95
95
96
96
private final Predicate <ShutdownSignalException > connectionRecoveryTriggeringCondition ;
97
97
98
+ private final RetryHandler retryHandler ;
99
+
98
100
public AutorecoveringConnection (ConnectionParams params , FrameHandlerFactory f , List <Address > addrs ) {
99
101
this (params , f , new ListAddressResolver (addrs ));
100
102
}
@@ -115,6 +117,8 @@ public AutorecoveringConnection(ConnectionParams params, FrameHandlerFactory f,
115
117
this .channels = new ConcurrentHashMap <>();
116
118
this .topologyRecoveryFilter = params .getTopologyRecoveryFilter () == null ?
117
119
letAllPassFilter () : params .getTopologyRecoveryFilter ();
120
+
121
+ this .retryHandler = params .getTopologyRecoveryRetryHandler ();
118
122
}
119
123
120
124
private void setupErrorOnWriteListenerForPotentialRecovery () {
@@ -125,12 +129,9 @@ private void setupErrorOnWriteListenerForPotentialRecovery() {
125
129
// we should trigger the error handling and the recovery only once
126
130
if (errorOnWriteLock .tryLock ()) {
127
131
try {
128
- Thread recoveryThread = threadFactory .newThread (new Runnable () {
129
- @ Override
130
- public void run () {
131
- AMQConnection c = (AMQConnection ) connection ;
132
- c .handleIoError (exception );
133
- }
132
+ Thread recoveryThread = threadFactory .newThread (() -> {
133
+ AMQConnection c = (AMQConnection ) connection ;
134
+ c .handleIoError (exception );
134
135
});
135
136
recoveryThread .setName ("RabbitMQ Error On Write Thread" );
136
137
recoveryThread .start ();
@@ -630,6 +631,10 @@ private void recoverChannels(final RecoveryAwareAMQConnection newConn) {
630
631
}
631
632
}
632
633
634
+ void recoverChannel (AutorecoveringChannel channel ) throws IOException {
635
+ channel .automaticallyRecover (this , this .delegate );
636
+ }
637
+
633
638
private void notifyRecoveryListenersComplete () {
634
639
for (RecoveryListener f : Utility .copy (this .recoveryListeners )) {
635
640
f .handleRecovery (this );
@@ -651,16 +656,16 @@ private void recoverTopology(final ExecutorService executor) {
651
656
if (executor == null ) {
652
657
// recover entities in serial on the main connection thread
653
658
for (final RecordedExchange exchange : Utility .copy (recordedExchanges ).values ()) {
654
- recoverExchange (exchange );
659
+ recoverExchange (exchange , true );
655
660
}
656
661
for (final Map .Entry <String , RecordedQueue > entry : Utility .copy (recordedQueues ).entrySet ()) {
657
- recoverQueue (entry .getKey (), entry .getValue ());
662
+ recoverQueue (entry .getKey (), entry .getValue (), true );
658
663
}
659
664
for (final RecordedBinding b : Utility .copy (recordedBindings )) {
660
- recoverBinding (b );
665
+ recoverBinding (b , true );
661
666
}
662
667
for (final Map .Entry <String , RecordedConsumer > entry : Utility .copy (consumers ).entrySet ()) {
663
- recoverConsumer (entry .getKey (), entry .getValue ());
668
+ recoverConsumer (entry .getKey (), entry .getValue (), true );
664
669
}
665
670
} else {
666
671
// Support recovering entities in parallel for connections that have a lot of queues, bindings, & consumers
@@ -680,11 +685,19 @@ private void recoverTopology(final ExecutorService executor) {
680
685
}
681
686
}
682
687
683
- private void recoverExchange (final RecordedExchange x ) {
688
+ private void recoverExchange (RecordedExchange x , boolean retry ) {
684
689
// recorded exchanges are guaranteed to be non-predefined (we filter out predefined ones in exchangeDeclare). MK.
685
690
try {
686
691
if (topologyRecoveryFilter .filterExchange (x )) {
687
- x .recover ();
692
+ if (retry ) {
693
+ final RecordedExchange entity = x ;
694
+ x = (RecordedExchange ) wrapRetryIfNecessary (x , () -> {
695
+ entity .recover ();
696
+ return null ;
697
+ }).getRecordedEntity ();
698
+ } else {
699
+ x .recover ();
700
+ }
688
701
LOGGER .debug ("{} has recovered" , x );
689
702
}
690
703
} catch (Exception cause ) {
@@ -695,12 +708,20 @@ private void recoverExchange(final RecordedExchange x) {
695
708
}
696
709
}
697
710
698
- private void recoverQueue (final String oldName , final RecordedQueue q ) {
699
711
712
+ void recoverQueue (final String oldName , RecordedQueue q , boolean retry ) {
700
713
try {
701
714
if (topologyRecoveryFilter .filterQueue (q )) {
702
715
LOGGER .debug ("Recovering {}" , q );
703
- q .recover ();
716
+ if (retry ) {
717
+ final RecordedQueue entity = q ;
718
+ q = (RecordedQueue ) wrapRetryIfNecessary (q , () -> {
719
+ entity .recover ();
720
+ return null ;
721
+ }).getRecordedEntity ();
722
+ } else {
723
+ q .recover ();
724
+ }
704
725
String newName = q .getName ();
705
726
if (!oldName .equals (newName )) {
706
727
// make sure server-named queues are re-added with
@@ -731,10 +752,18 @@ private void recoverQueue(final String oldName, final RecordedQueue q) {
731
752
}
732
753
}
733
754
734
- private void recoverBinding (final RecordedBinding b ) {
755
+ private void recoverBinding (RecordedBinding b , boolean retry ) {
735
756
try {
736
757
if (this .topologyRecoveryFilter .filterBinding (b )) {
737
- b .recover ();
758
+ if (retry ) {
759
+ final RecordedBinding entity = b ;
760
+ b = (RecordedBinding ) wrapRetryIfNecessary (b , () -> {
761
+ entity .recover ();
762
+ return null ;
763
+ }).getRecordedEntity ();
764
+ } else {
765
+ b .recover ();
766
+ }
738
767
LOGGER .debug ("{} has recovered" , b );
739
768
}
740
769
} catch (Exception cause ) {
@@ -745,11 +774,20 @@ private void recoverBinding(final RecordedBinding b) {
745
774
}
746
775
}
747
776
748
- private void recoverConsumer (final String tag , final RecordedConsumer consumer ) {
777
+ private void recoverConsumer (final String tag , RecordedConsumer consumer , boolean retry ) {
749
778
try {
750
779
if (this .topologyRecoveryFilter .filterConsumer (consumer )) {
751
780
LOGGER .debug ("Recovering {}" , consumer );
752
- String newTag = consumer .recover ();
781
+ String newTag = null ;
782
+ if (retry ) {
783
+ final RecordedConsumer entity = consumer ;
784
+ RetryResult retryResult = wrapRetryIfNecessary (consumer , () -> entity .recover ());
785
+ consumer = (RecordedConsumer ) retryResult .getRecordedEntity ();
786
+ newTag = (String ) retryResult .getResult ();
787
+ } else {
788
+ newTag = consumer .recover ();
789
+ }
790
+
753
791
// make sure server-generated tags are re-added. MK.
754
792
if (tag != null && !tag .equals (newTag )) {
755
793
synchronized (this .consumers ) {
@@ -772,6 +810,33 @@ private void recoverConsumer(final String tag, final RecordedConsumer consumer)
772
810
}
773
811
}
774
812
813
+ private <T > RetryResult wrapRetryIfNecessary (RecordedEntity entity , Callable <T > recoveryAction ) throws Exception {
814
+ if (this .retryHandler == null ) {
815
+ T result = recoveryAction .call ();
816
+ return new RetryResult (entity , result );
817
+ } else {
818
+ try {
819
+ T result = recoveryAction .call ();
820
+ return new RetryResult (entity , result );
821
+ } catch (Exception e ) {
822
+ RetryContext retryContext = new RetryContext (entity , e , this );
823
+ RetryResult retryResult ;
824
+ if (entity instanceof RecordedQueue ) {
825
+ retryResult = this .retryHandler .retryQueueRecovery (retryContext );
826
+ } else if (entity instanceof RecordedExchange ) {
827
+ retryResult = this .retryHandler .retryExchangeRecovery (retryContext );
828
+ } else if (entity instanceof RecordedBinding ) {
829
+ retryResult = this .retryHandler .retryBindingRecovery (retryContext );
830
+ } else if (entity instanceof RecordedConsumer ) {
831
+ retryResult = this .retryHandler .retryConsumerRecovery (retryContext );
832
+ } else {
833
+ throw new IllegalArgumentException ("Unknown type of recorded entity: " + entity );
834
+ }
835
+ return retryResult ;
836
+ }
837
+ }
838
+ }
839
+
775
840
private void propagateQueueNameChangeToBindings (String oldName , String newName ) {
776
841
for (RecordedBinding b : Utility .copy (this .recordedBindings )) {
777
842
if (b .getDestination ().equals (oldName )) {
@@ -820,15 +885,15 @@ private <E extends RecordedEntity> List<Callable<Object>> groupEntitiesByChannel
820
885
callables .add (Executors .callable (() -> {
821
886
for (final E entity : entityList ) {
822
887
if (entity instanceof RecordedExchange ) {
823
- recoverExchange ((RecordedExchange )entity );
888
+ recoverExchange ((RecordedExchange )entity , true );
824
889
} else if (entity instanceof RecordedQueue ) {
825
890
final RecordedQueue q = (RecordedQueue ) entity ;
826
- recoverQueue (q .getName (), q );
891
+ recoverQueue (q .getName (), q , true );
827
892
} else if (entity instanceof RecordedBinding ) {
828
- recoverBinding ((RecordedBinding ) entity );
893
+ recoverBinding ((RecordedBinding ) entity , true );
829
894
} else if (entity instanceof RecordedConsumer ) {
830
895
final RecordedConsumer c = (RecordedConsumer ) entity ;
831
- recoverConsumer (c .getConsumerTag (), c );
896
+ recoverConsumer (c .getConsumerTag (), c , true );
832
897
}
833
898
}
834
899
}));
0 commit comments