@@ -95,6 +95,8 @@ public class AutorecoveringConnection implements RecoverableConnection, NetworkC
9595
9696 private final Predicate <ShutdownSignalException > connectionRecoveryTriggeringCondition ;
9797
98+ private final RetryHandler retryHandler ;
99+
98100 public AutorecoveringConnection (ConnectionParams params , FrameHandlerFactory f , List <Address > addrs ) {
99101 this (params , f , new ListAddressResolver (addrs ));
100102 }
@@ -115,6 +117,8 @@ public AutorecoveringConnection(ConnectionParams params, FrameHandlerFactory f,
115117 this .channels = new ConcurrentHashMap <>();
116118 this .topologyRecoveryFilter = params .getTopologyRecoveryFilter () == null ?
117119 letAllPassFilter () : params .getTopologyRecoveryFilter ();
120+
121+ this .retryHandler = params .getTopologyRecoveryRetryHandler ();
118122 }
119123
120124 private void setupErrorOnWriteListenerForPotentialRecovery () {
@@ -125,12 +129,9 @@ private void setupErrorOnWriteListenerForPotentialRecovery() {
125129 // we should trigger the error handling and the recovery only once
126130 if (errorOnWriteLock .tryLock ()) {
127131 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 );
134135 });
135136 recoveryThread .setName ("RabbitMQ Error On Write Thread" );
136137 recoveryThread .start ();
@@ -630,6 +631,10 @@ private void recoverChannels(final RecoveryAwareAMQConnection newConn) {
630631 }
631632 }
632633
634+ void recoverChannel (AutorecoveringChannel channel ) throws IOException {
635+ channel .automaticallyRecover (this , this .delegate );
636+ }
637+
633638 private void notifyRecoveryListenersComplete () {
634639 for (RecoveryListener f : Utility .copy (this .recoveryListeners )) {
635640 f .handleRecovery (this );
@@ -651,16 +656,16 @@ private void recoverTopology(final ExecutorService executor) {
651656 if (executor == null ) {
652657 // recover entities in serial on the main connection thread
653658 for (final RecordedExchange exchange : Utility .copy (recordedExchanges ).values ()) {
654- recoverExchange (exchange );
659+ recoverExchange (exchange , true );
655660 }
656661 for (final Map .Entry <String , RecordedQueue > entry : Utility .copy (recordedQueues ).entrySet ()) {
657- recoverQueue (entry .getKey (), entry .getValue ());
662+ recoverQueue (entry .getKey (), entry .getValue (), true );
658663 }
659664 for (final RecordedBinding b : Utility .copy (recordedBindings )) {
660- recoverBinding (b );
665+ recoverBinding (b , true );
661666 }
662667 for (final Map .Entry <String , RecordedConsumer > entry : Utility .copy (consumers ).entrySet ()) {
663- recoverConsumer (entry .getKey (), entry .getValue ());
668+ recoverConsumer (entry .getKey (), entry .getValue (), true );
664669 }
665670 } else {
666671 // 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) {
680685 }
681686 }
682687
683- private void recoverExchange (final RecordedExchange x ) {
688+ private void recoverExchange (RecordedExchange x , boolean retry ) {
684689 // recorded exchanges are guaranteed to be non-predefined (we filter out predefined ones in exchangeDeclare). MK.
685690 try {
686691 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+ }
688701 LOGGER .debug ("{} has recovered" , x );
689702 }
690703 } catch (Exception cause ) {
@@ -695,12 +708,20 @@ private void recoverExchange(final RecordedExchange x) {
695708 }
696709 }
697710
698- private void recoverQueue (final String oldName , final RecordedQueue q ) {
699711
712+ void recoverQueue (final String oldName , RecordedQueue q , boolean retry ) {
700713 try {
701714 if (topologyRecoveryFilter .filterQueue (q )) {
702715 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+ }
704725 String newName = q .getName ();
705726 if (!oldName .equals (newName )) {
706727 // make sure server-named queues are re-added with
@@ -731,10 +752,18 @@ private void recoverQueue(final String oldName, final RecordedQueue q) {
731752 }
732753 }
733754
734- private void recoverBinding (final RecordedBinding b ) {
755+ private void recoverBinding (RecordedBinding b , boolean retry ) {
735756 try {
736757 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+ }
738767 LOGGER .debug ("{} has recovered" , b );
739768 }
740769 } catch (Exception cause ) {
@@ -745,11 +774,20 @@ private void recoverBinding(final RecordedBinding b) {
745774 }
746775 }
747776
748- private void recoverConsumer (final String tag , final RecordedConsumer consumer ) {
777+ private void recoverConsumer (final String tag , RecordedConsumer consumer , boolean retry ) {
749778 try {
750779 if (this .topologyRecoveryFilter .filterConsumer (consumer )) {
751780 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+
753791 // make sure server-generated tags are re-added. MK.
754792 if (tag != null && !tag .equals (newTag )) {
755793 synchronized (this .consumers ) {
@@ -772,6 +810,33 @@ private void recoverConsumer(final String tag, final RecordedConsumer consumer)
772810 }
773811 }
774812
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+
775840 private void propagateQueueNameChangeToBindings (String oldName , String newName ) {
776841 for (RecordedBinding b : Utility .copy (this .recordedBindings )) {
777842 if (b .getDestination ().equals (oldName )) {
@@ -820,15 +885,15 @@ private <E extends RecordedEntity> List<Callable<Object>> groupEntitiesByChannel
820885 callables .add (Executors .callable (() -> {
821886 for (final E entity : entityList ) {
822887 if (entity instanceof RecordedExchange ) {
823- recoverExchange ((RecordedExchange )entity );
888+ recoverExchange ((RecordedExchange )entity , true );
824889 } else if (entity instanceof RecordedQueue ) {
825890 final RecordedQueue q = (RecordedQueue ) entity ;
826- recoverQueue (q .getName (), q );
891+ recoverQueue (q .getName (), q , true );
827892 } else if (entity instanceof RecordedBinding ) {
828- recoverBinding ((RecordedBinding ) entity );
893+ recoverBinding ((RecordedBinding ) entity , true );
829894 } else if (entity instanceof RecordedConsumer ) {
830895 final RecordedConsumer c = (RecordedConsumer ) entity ;
831- recoverConsumer (c .getConsumerTag (), c );
896+ recoverConsumer (c .getConsumerTag (), c , true );
832897 }
833898 }
834899 }));
0 commit comments