简体   繁体   中英

Spring AMQP - dead letter implementation and max retry if business logic fails

Using Spring AMQP - spring-amqp with Spring Boot

Here I am trying to implement dead letter exchange. I am sending message to a queue, if some business exception occurs then it should send the message to "dlq" queue and wait there for 5 seconds and then it should come into queue for processing again..... After 5 try it should come out from container.

Please find the configruation

application.yml

spring:
  rabbitmq:
    host: localhost
    username: guest
    password: guest
    port: 5672
server:
port: 8081

MQ Config

import java.util.HashMap;
import java.util.Map;
import org.springframework.amqp.core.Binding;
 import org.springframework.amqp.core.BindingBuilder;
import org.springframework.amqp.core.DirectExchange;
import org.springframework.amqp.core.Queue;
import org.springframework.amqp.rabbit.config.SimpleRabbitListenerContainerFactory;
import org.springframework.amqp.rabbit.connection.ConnectionFactory;
import org.springframework.amqp.rabbit.core.RabbitTemplate;
import org.springframework.amqp.support.converter.Jackson2JsonMessageConverter;
 import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;


@Configuration
public class MQConfig {

// public static final String OUTGOING_QUEUE = "outgoing.example";

  // public static final String INCOMING_QUEUE = "incoming.example";

  @Autowired
  private ConnectionFactory cachingConnectionFactory;

  // Setting the annotation listeners to use the jackson2JsonMessageConverter
  @Bean
  public SimpleRabbitListenerContainerFactory rabbitListenerContainerFactory() {
    SimpleRabbitListenerContainerFactory factory = new SimpleRabbitListenerContainerFactory();
    factory.setConnectionFactory(cachingConnectionFactory);
    factory.setMessageConverter(jackson2JsonMessageConverter());
    return factory;
  }

  // Standardize on a single objectMapper for all message queue items
  @Bean
  public Jackson2JsonMessageConverter jackson2JsonMessageConverter() {
    return new Jackson2JsonMessageConverter();
  }

  @Bean
  public Queue outgoingQueue() {
    Map<String, Object> args = new HashMap<String, Object>();
    // The default exchange
    args.put("x-dead-letter-exchange", "dlx");
    // Route to the incoming queue when the TTL occurs
    // args.put("x-dead-letter-routing-key", "q.with.dlx");
    // TTL 5 seconds
    args.put("x-message-ttl", 5000);
    return new Queue("q.with.dlx", false, false, false, args);
  }

  @Bean
  public RabbitTemplate rabbitTemplate() {
    RabbitTemplate rabbitTemplate = new RabbitTemplate(cachingConnectionFactory);
    rabbitTemplate.setMessageConverter(jackson2JsonMessageConverter());
    return rabbitTemplate;
  }

  @Bean
  public Queue incomingQueue() {
    return new Queue("dlq");
  }

  @Bean
  public DirectExchange directExchange() {
      return new DirectExchange("dlx") ; 
  }

  @Bean
  public Binding binding(Queue incomingQueue, DirectExchange directExchange) {
      return BindingBuilder.bind(incomingQueue()).to(directExchange()).with("q.with.dlx");
  }

Publisher

import java.util.Date;
import org.springframework.amqp.rabbit.core.RabbitTemplate;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import com.spring.amqp.domain.ExampleObject;

@Component
@RestController
@RequestMapping("/publish")
public class Publisher {

@Autowired
private RabbitTemplate rabbitTemplate;

// Scheduled task to send an object every 5 seconds
// @Scheduled(fixedDelay = 5000)
@GetMapping()
public void sender() {
    ExampleObject ex = new ExampleObject();
    ex.setDate(new Date());
    rabbitTemplate.convertAndSend("q.with.dlx",ex);
}
}

Consumer

package com.spring.amqp.service;
import java.util.List;
import org.springframework.amqp.AmqpRejectAndDontRequeueException;
import org.springframework.amqp.rabbit.annotation.RabbitListener;
import org.springframework.messaging.handler.annotation.Header;
import org.springframework.stereotype.Component;
import com.spring.amqp.domain.ExampleObject;

@Component
public class Consumer {

// Annotation to listen for an ExampleObject
@RabbitListener(queues = "q.with.dlx")
public void handleMessage(ExampleObject exampleObject,
        @Header(required = false, name = "x-death") List<String> xDeath) {
    System.out.println("Message" + ":" + (xDeath == null ? "" : xDeath));
    System.out.println("Received incoming object at " + exampleObject.getDate());


    // // String x_header_count = xDeath.get("count");
    // System.out.println(x_header_count);
    try{
    int a = 5 / 0;
    System.out.println(a);
    }
    catch(Exception ex) {
        throw new AmqpRejectAndDontRequeueException("amqp exception") ;
    }


}
}

**in x-header I am getting nothing. ** Now when I am hitting localhost:8081/publish, it sends a message to q.with.dlx and I am throwing AmqpRejectAndRequeue exception and then that message is getting struck in the "dlq" named queue. Nothing happens after that. I have one domain object named ExampleObject which I am sending from publisher to consumer.

Please cross check all my configuration and if possible can someone run this and let me know what is the mistake? Thanks in advance.

And Gary Russell Thanks for this awesome messaging framework.

You have the TTL on the wrong queue.

You need to configure time-to-live dead-lettering on your dlq:

  @Bean
  public Queue incomingQueue() {
      return new Queue("dlq");
  }

Add arguments x-message-ttl = 5000 and dead-letter configuration to route the expired message back to the original queue.

Your queue bean names are a bit confusing; you would normally have something like...

someExchange -> mainQueue (with dead-lettering to DLX)

DLX -> dlq (with TTL and dead-lettering to someExchange)

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM