Spring AMQP中的消息重试策略

时间:2018-01-02 05:50:29

标签: spring-mvc spring-amqp spring-rabbitmq

我正在使用spring-amqp在我的Web应用程序中使用来自RabbitMQ的消息。 Web应用程序由多个组件组成,例如( Redis,OracleDB

现在我有一个场景,如果由于 Oracle服务器关闭,Redis连接问题等基础设施发生任何异常,我想将消息推回到同一队列并且在某些指定之后延迟我想要回消消息。

在一定延迟之后,该消息也会导致相同的异常,可能我想使用最大尝试次数或者按照上面的相同操作将消息推回队列并向管理员发送邮件说明 "基础设施问题"

Spring AMQP是否支持上述场景。如果是,请告诉我如何提出这样或类似的解决方案。

我在下面的代码中尝试过。消息不会用于死信队列,而是重新排队到同一队列导致无限循环。请纠正我出错的地方

配置类

    @Configuration
public class MQConfig {

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

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

    public static final String DEAD_LETTER_QUEUE = "my.deadletter.queue.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());
        factory.setDefaultRequeueRejected(false);
        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>();
        args.put("x-dead-letter-exchange", "dlx");
        args.put("x-dead-letter-routing-key", DEAD_LETTER_QUEUE);
        args.put("x-message-ttl", 50000);
        return new Queue(OUTGOING_QUEUE, false, false, false, args);
    }

    @Bean
    public RabbitTemplate outgoingSender() {
        RabbitTemplate rabbitTemplate = new RabbitTemplate(cachingConnectionFactory);
        rabbitTemplate.setQueue(outgoingQueue().getName());
        // rabbitTemplate.setRoutingKey(outgoingQueue().getName());
        rabbitTemplate.setMessageConverter(jackson2JsonMessageConverter());
        return rabbitTemplate;
    }

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

    @Bean
    public Queue deadLetterQueue() {
        return new Queue(DEAD_LETTER_QUEUE);
    }

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

    @Bean
    public Binding dlqBinding() {
        return BindingBuilder.bind(deadLetterQueue()).to(dlx()).with(DEAD_LETTER_QUEUE);
    }

}

核心逻辑

@Component
public class DeadLetterSendReceive {

  private static final Logger LOGGER = LoggerFactory.getLogger(DeadLetterSendReceive.class);

  @Autowired
  private RabbitTemplate outgoingSender;

  // Scheduled task to send an object every 5 seconds
  @Scheduled(fixedDelay = 5000)
  public void sender() {

    Integer int1[] = new Integer[]{10,20,30,40,50};  
    for (int i = 0; i <= int1.length; i++){
        System.out.println(int1[i]);

        if(int1[i]/10 == 1){
            throw new AmqpRejectAndDontRequeueException("to deadletter queue");
        }
        else{
            ExampleObject ex = new ExampleObject();
            ex.setValue(int1[i]);
             LOGGER.info("Sending example object at " + ex.getValue());
             outgoingSender.convertAndSend(ex);
        }

    }
  }

  // Annotation to listen for an ExampleObject
  @RabbitListener(queues = MQConfig.INCOMING_QUEUE)
  public void handleMessage(ExampleObject exampleObject) {
    LOGGER.info("Received incoming object at " + exampleObject.getValue());
  }

}

Pojo Class

import java.util.Date;

public class ExampleObject {

  private Date date = new Date();
  private int value;

  public int getValue() {
    return value;
}

public void setValue(int value) {
    this.value = value;
}

public ExampleObject() {
  }

  @Override
  public String toString() {
    return "ExampleObject{" +
        "date= " + date +
        '}';
  }

  public Date getDate() {
    return date;
  }

  public void setDate(Date date) {
    this.date = date;
  }

}

1 个答案:

答案 0 :(得分:1)

有几种方法可以做到;使用delayed message exchange plugin并将失败的消息发布到它。您可以设置标题以跟踪已尝试的次数。

或者您可以使用带有TTL的死信队列来执行此操作,其中死信队列配置为dead-lettering,以将过期的消息发送回原始队列。请参阅my answer to this question及其与其他答案的链接。

您可以使用x-death标题来跟踪重试次数;它在最近的经纪人中已被更改为现在保持计数而不是继续向标题添加新条目。

要强制邮件转到DLQ,请将defaultRequeueRejected设置为false或抛出AmqpRejectAndDontRequeueException