Search code examples
javaspring-bootrabbitmqspring-amqpspring-rabbit

Rabbit MQ + Spring Boot: delay between resend broken messages


I'm creating application using Spring Boot with RabbitMQ. I've created configuration for Rabbit like this:

@Configuration
public class RabbitConfiguration {
    public static final String RESEND_DISPOSAL_QUEUE = "RESEND_DISPOSAL";

    @Bean
    public Queue resendDisposalQueue() {
        return new Queue(RESEND_DISPOSAL_QUEUE, true);
    }

    @Bean
    public SimpleRabbitListenerContainerFactory rabbitListenerContainerFactory (ConnectionFactory connectionFactoryr) {
        SimpleRabbitListenerContainerFactory factory = new SimpleRabbitListenerContainerFactory();
        factory.setConnectionFactory(connectionFactory);
        return factory;
    }

    @Bean
    public RabbitTemplate rabbitTemplate(ConnectionFactory connectionFactory){
        return new RabbitTemplate(connectionFactory);
    }
}

Also I've created listener for Rabbit messages like this:

@RabbitListener(queues = RESEND_DISPOSAL_QUEUE)
public void getResendDisposalPayload(String messageBody){
    LOGGER.info("[getResendDisposalPayload] message = {}", messageBody);
    // And there is some business logic
}

All works pretty good, but there is one problem. When I got exception in method getResendDisposalPayload which listens RESEND_DISPOSAL_QUEUE queue (for example temporary problem with database) Rabbit starts resend last not processed message without any delay. It produces a big amount of log and for some reason uncomfortable for my system.

As I've read in this article https://www.baeldung.com/spring-amqp-exponential-backoff "While using a Dead Letter Queue is a standard way to deal with failed messages". In order to use this pattern I've to create RetryOperationsInterceptor which defines count attempt to deliver message and delay between attempts. For example:

@Bean
public RetryOperationsInterceptor retryInterceptor() {
    return RetryInterceptorBuilder.stateless()
            .backOffOptions(1000, 3.0, 10000)
            .maxAttempts(3)
            .recoverer(messageRecoverer)
            .build();
}

It sounds very good but only one problem: I can't define infinity attempt amount in options maxAttempts. After maxAttempts I have to save somewhere broken message and deal with it in the future. It demands some extra code.

The question is: Is there any way to configure Rabbit to infinity resend broken messages with some delay, say with one second delay?


Solution

  • Rabbit starts resend last not processed message without any delay

    That's how redelivery works: it re-push the same message again and again, until you ack it manually or drop altogether. There is no delay in between redeliveries just because an new message is not pulled from the queue until something is done with this one.

    I can't define infinity attempt amount in options maxAttempts

    Have you tried an Integer.MAX_VALUE? Pretty decent number of attempts.

    The other way is to use a Delayed Exchange: https://docs.spring.io/spring-amqp/docs/current/reference/html/#delayed-message-exchange.

    You can configure that retry with a RepublishMessageRecoverer to publish into a your original queue back after some attempts are exhausted: https://docs.spring.io/spring-amqp/docs/current/reference/html/#async-listeners