Search code examples
rabbitmqspring-amqpspring-rabbitretrypolicy

spring amqp enable retry by default and prevent it according to a specified exception


In case of Exception A : retrying for finite number of times and finally when number of retrials exhausted, message is written in a dead letter queue

In case of Exception B : simply, message should be written to dead letter queue

I am trying to implement the same use case as in and i have followed all the steps as per correct answer, still i see my custom exception getting wrapped into ListenerExecutionFailedException. I am unable to get it stop retrying on a custom exception.

I have followed steps in below answers spring amqp enable retry by configuration and prevent it according to a specified exception

and

Spring rabbit retries to deliver rejected message..is it OK?


@Bean
    public SimpleRetryPolicy rejectionRetryPolicy(){
        Map<Class<? extends Throwable> , Boolean> exceptionsMap = new HashMap<Class<? extends Throwable> , Boolean>();        
        exceptionsMap.put(AmqpRejectAndDontRequeueException.class, false);//not retriable
        exceptionsMap.put(ListenerExecutionFailedException.class, true); //retriable
        SimpleRetryPolicy retryPolicy = new SimpleRetryPolicy(3 , exceptionsMap ,true);
        return retryPolicy;
    }

    @Bean
    public RetryOperationsInterceptor workMessagesRetryInterceptor() {
        return RetryInterceptorBuilder.stateless().retryPolicy(rejectionRetryPolicy())

                //.backOffOptions(1000, 2, 10000)
                .recoverer(new RepublishMessageRecoverer(defaultTemplate, this.getDlqExchange(), this.getDlqroutingkey()))
                .build();
    }


/* My Rabbit MQ Error handler */

@Component
public class RabbitRetryHandler implements RabbitListenerErrorHandler {

    private static final Logger log = LoggerFactory.getLogger(RabbitRetryHandler.class);

    @Override
    public Object handleError(Message amqpMessage, org.springframework.messaging.Message<?> message,
            ListenerExecutionFailedException exception) throws Exception {      
        if (amqpMessage.getMessageProperties().isRedelivered() || exception.getCause().getMessage().equals("DontRetry")) {          
            throw new AmqpRejectAndDontRequeueException(exception.getCause());
        } else {
            throw exception;
        }
    }
}

/* And finally my Listener */

@Override
    @RabbitListener(queues = "${queueconfig.queuename}",containerFactory = "sdRabbitListenerContainerFactory",errorHandler="rabbitRetryHandler")
    public void processMessage(Message incomingMsg) throws Exception {
        log.info("{} - Correlation ID: {} Received message: {} from {} queue.", Thread.currentThread().getId(),
                incomingMsg.getMessageProperties().getCorrelationId(), new String(incomingMsg.getBody()),
                incomingMsg.getMessageProperties().getConsumerQueue());
        try {
            performAction();
        } catch(CustomDontRequeueException cex) {
            throw cex;
        } catch (Exception ex) {
            throw ex;
        }
    }


@Override
    public void performAction() throws Exception {
        try {

        } catch (HttpClientErrorException ex) {
            if (ex.getStatusCode() == HttpStatus.NOT_FOUND || ex.getStatusCode() == HttpStatus.REQUEST_TIMEOUT) {

                throw new RuntimeException(ex);
            } else {
                throw new CustomDontRequeueException("DontRetry",ex);
            }
        }catch (Exception e) {          
            throw new CustomDontRequeueException(e);
        } 

    }

Expected Result, if CustomDontRequeueException is thrown it should not requeue the message.

Actual Result, message gets requeued irrespective of what exception it is for n number of times and then dropped to DLQ.


Solution

  •     exceptionsMap.put(AmqpRejectAndDontRequeueException.class, false);//not retriable
        exceptionsMap.put(ListenerExecutionFailedException.class, true); //retriable
    

    You haven't configured CustomDontRequeueException to not retry, you have configured AmqpRejectAndDontRequeueException.

    Also, you should not explicitly set ListenerExecutionFailedException because it will be found first, preventing cause traversal.