Search code examples
spring-bootrabbitmqspring-amqpspring-rabbit

RabbitHandler to create consumer and retry on Fatal Exception in Spring for queue on listening to RabbitMQ


I am using Spring AMQP RabbitHandler and have written the following code:

@RabbitListener(queues = "#{testQueue.name}")
public class Tut4Receiver {

  @RabbitHandler
  public void receiveMessage(String message){
       System.out.println("Message received "+message);
  }
}

The Queue is defined like:-

@Bean
public Queue testQueue() {
    return new AnonymousQueue();
}

I am using separate code to initialize the Connection Factory.

My question is if RabbitMQ is down for some time, it keeps on retrying to create a consumer but only if it receives a ConnectionRefused error. But suppose the user does not exist in RabbitMQ and there is a gap in which a new user will be created, then it receives a fatal error from RabbitMQ and it never retries due to which the result is auto delete queue would be created on RabbitMQ without any consumers.

Stack Trace:

    SimpleMessageListenerContainer] [SimpleAsyncTaskExecutor-11] [|] [|||] Consumer received fatal exception on startup 
org.springframework.amqp.rabbit.listener.exception.FatalListenerStartupException: Authentication failure
    at org.springframework.amqp.rabbit.listener.BlockingQueueConsumer.start(BlockingQueueConsumer.java:476)
    at org.springframework.amqp.rabbit.listener.SimpleMessageListenerContainer$AsyncMessageProcessingConsumer.run(SimpleMessageListenerContainer.java:1280)
    at java.lang.Thread.run(Thread.java:748)
Caused by: org.springframework.amqp.AmqpAuthenticationException: com.rabbitmq.client.AuthenticationFailureException: ACCESS_REFUSED - Login was refused using     authentication mechanism PLAIN. For details see the broker logfile.
    at org.springframework.amqp.rabbit.support.RabbitExceptionTranslator.convertRabbitAccessException(RabbitExceptionTranslator.java:65)
    at org.springframework.amqp.rabbit.connection.AbstractConnectionFactory.createBareConnection(AbstractConnectionFactory.java:309)
    at org.springframework.amqp.rabbit.connection.CachingConnectionFactory.createConnection(CachingConnectionFactory.java:547)
    at org.springframework.amqp.rabbit.connection.ConnectionFactoryUtils$1.createConnection(ConnectionFactoryUtils.java:90)
    at org.springframework.amqp.rabbit.connection.ConnectionFactoryUtils.doGetTransactionalResourceHolder(ConnectionFactoryUtils.java:140)
    at org.springframework.amqp.rabbit.connection.ConnectionFactoryUtils.getTransactionalResourceHolder(ConnectionFactoryUtils.java:76)
    at org.springframework.amqp.rabbit.listener.BlockingQueueConsumer.start(BlockingQueueConsumer.java:472)
    ... 2 common frames omitted
Caused by: com.rabbitmq.client.AuthenticationFailureException: ACCESS_REFUSED - Login was refused using authentication mechanism PLAIN. For details see the broker     logfile.
    at com.rabbitmq.client.impl.AMQConnection.start(AMQConnection.java:339)
    at com.rabbitmq.client.ConnectionFactory.newConnection(ConnectionFactory.java:813)
    at com.rabbitmq.client.ConnectionFactory.newConnection(ConnectionFactory.java:767)
    at com.rabbitmq.client.ConnectionFactory.newConnection(ConnectionFactory.java:887)
    at org.springframework.amqp.rabbit.connection.AbstractConnectionFactory.createBareConnection(AbstractConnectionFactory.java:300)

 SimpleMessageListenerContainer] [SimpleAsyncTaskExecutor-11] [|] [|||] Stopping container from aborted consumer 
  [|] [|||] Waiting for workers to finish. 
  [|] [|||] Successfully waited for workers to finish.

Any way to retry even on fatal exceptions like when the user does not exist?


Solution

  • Authentication failures are considered fatal by default and not retried.

    You can override this behavior by setting a property on the listener container (possibleAuthenticationFailureFatal). The property is not available as a boot property so you have to override boot's container factory...

        @Bean(name = "rabbitListenerContainerFactory")
        public SimpleRabbitListenerContainerFactory simpleRabbitListenerContainerFactory(
                SimpleRabbitListenerContainerFactoryConfigurer configurer, ConnectionFactory connectionFactory) {
    
            SimpleRabbitListenerContainerFactory factory = new SimpleRabbitListenerContainerFactory();
            configurer.configure(factory, connectionFactory);
            factory.setContainerConfigurer(smlc -> smlc.setPossibleAuthenticationFailureFatal(false));
            return factory;
        }