RabbitMQ 消息丢失案例(returnCallback/confirmCallback)

  • Post author:
  • Post category:其他




源码在文章末尾👇🏻


前置学习知识


SpringAMQP 简单入门demo


FanoutExchange交换机代码教程


DirectExchange交换机简单入门demo

1. 配置文件

logging:
  pattern:
    dateformat: MM-dd HH:mm:ss:SSS
spring:
  rabbitmq:
    host: localhost # rabbitMQ的ip地址
    port: 5672 # 端口
    username: guest
    password: guest
    virtual-host: /
    publisher-confirm-type: correlated
    publisher-returns: true
    template:
      mandatory: true


  • publish-confirm-type

    :开启publisher-confirm,这里支持两种类型:



    • simple

      :同步等待confirm结果,直到超时



    • correlated

      :异步回调,定义ConfirmCallback,MQ返回结果时会回调这个ConfirmCallback



  • publish-returns

    :开启publish-return功能,同样是基于callback机制,不过是定义ReturnCallback



  • template.mandatory

    :定义消息路由失败时的策略。true,则调用ReturnCallback;false:则直接丢弃消息

2. 定义回调

package cn.itcast.mq.config;

import lombok.extern.slf4j.Slf4j;
import org.springframework.amqp.rabbit.core.RabbitTemplate;
import org.springframework.beans.BeansException;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;
import org.springframework.context.annotation.Configuration;

@Slf4j
@Configuration
public class CommonConfig implements ApplicationContextAware {
    @Override
    public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
        // 获取RabbitTemplate
        RabbitTemplate rabbitTemplate = applicationContext.getBean(RabbitTemplate.class);
        // 设置ReturnCallback
        rabbitTemplate.setReturnCallback((message, replyCode, replyText, exchange, routingKey) -> {
            // 投递失败,记录日志
            log.info("消息发送失败,应答码{},原因{},交换机{},路由键{},消息{}",
                     replyCode, replyText, exchange, routingKey, message.toString());
            // 如果有业务需要,可以重发消息
        });
    }
}

3.定义ConfirmCallback

public void testSendMessage2SimpleQueue() throws InterruptedException {
    // 1.消息体
    String message = "hello, spring amqp!";
    // 2.全局唯一的消息ID,需要封装到CorrelationData中
    CorrelationData correlationData = new CorrelationData(UUID.randomUUID().toString());
    // 3.添加callback
    correlationData.getFuture().addCallback(
        result -> {
            if(result.isAck()){
                // 3.1.ack,消息成功
                log.debug("消息发送成功, ID:{}", correlationData.getId());
            }else{
                // 3.2.nack,消息失败
                log.error("消息发送失败, ID:{}, 原因{}",correlationData.getId(), result.getReason());
            }
        },
        ex -> log.error("消息发送异常, ID:{}, 原因{}",correlationData.getId(),ex.getMessage())
    );
    // 4.发送消息
    rabbitTemplate.convertAndSend("task.direct", "task", message, correlationData);

    // 休眠一会儿,等待ack回执
    Thread.sleep(2000);
}

4. 编写消费者代码(采用direct交换机)

    @RabbitListener(bindings = @QueueBinding(
            value = @Queue("direct.queue2"),
            exchange = @Exchange(value = "direct"),
            key = {"red", "yellow"}
    ))
    public void listenDirectQueue2(String msg){
        System.out.println("消费者接收到fanout.queue2的消息:【" + msg + "】");
    }

5. 消息发送失败的两种场景


(1)消息并未发送到交换机里(


confirmCallback


)


这种情况下就是交换机的名称填写错误导致消息无法投递到交换机上


(2)成功投递到了交换机, 但是并未到达队列(


returnCallback


)

这次是我们的交换机名称正确, 而且成功投递到了交换机,

但是由于routingKey的名称写错了,就走了我们定义的Callback当中

链接: https://pan.baidu.com/s/1a2VwGh3_3Kg4WGkZgp0JVw 提取码: pj40



版权声明:本文为qq_45481709原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。