一、前言
在使用 RabbitMQ 的时候,作为消息发送方希望杜绝任何消息丢失或者投递失败场景。RabbitMQ 为我们提供了两种方式用来控制消息的投递可靠性模式。
-
confirm 确认模式
-
return 退回模式
先了解rabbitmq 整个消息投递的路径:producer—>rabbitmq broker—>exchange—>queue—>consumer
-
消息从 producer 到 exchange 则会返回一个 confirmCallback 。
-
消息从 exchange–>queue 投递失败则会返回一个 returnCallback 。
简而言之,confirm确认消息是否从生产者到交换机,return确认消息是否从交换机到队列,我们将利用这两个 callback 控制消息的可靠性投递。
二、添加配置
-
导入依赖省略
-
添加rabbitmq配置,消费者publisher-confirm-type、publisher-returns不用配置
spring: rabbitmq: host: 127.0.0.1 port: 5672 username: nov17 password: nov17 virtual-host: testhost publisher-confirm-type: correlated #设置confirms type publisher-returns: true #开启return确认模式
三、生产者代码
-
创建RabbitMQ队列配置类com.example.rabbitmq_producer.config;
@Configuration public class RabbitMQConfig { //交换机名称 public static final String ITEM_EXCHANGE = "item_exchange"; //队列名称 public static final String ITEM_QUEUE = "item_queue"; //声明交换机 @Bean("itemExchange") public Exchange itemExchange(){ return ExchangeBuilder.directExchange(ITEM_EXCHANGE).durable(true).build(); } //声明队列 @Bean("itemQueue") public Queue itemQueue(){ return QueueBuilder.durable(ITEM_QUEUE).build(); } //绑定队列和交换机 @Bean public Binding itemQueueExchange(@Qualifier("itemQueue") Queue queue, @Qualifier("itemExchange") Exchange exchange){ return BindingBuilder.bind(queue).to(exchange).with("info").noargs(); } }
-
在生产者工程springboot-rabbitmq-producer中创建测试类,发送消息;
@SpringBootTest public class RabbitMQTest { @Autowired private RabbitTemplate rabbitTemplate; @Test public void test(){ // true:RabbitMQ会调用Basic.Return命令将消息返回给生产者 // false:RabbitMQ会把消息直接丢弃 rabbitTemplate.setMandatory(true); //设置ReturnsCallback rabbitTemplate.setReturnsCallback( returnedMessage -> { //消息对象 Message message = returnedMessage.getMessage(); //错误码 int replyCode = returnedMessage.getReplyCode(); //错误信息 String replyText = returnedMessage.getReplyText(); //交换机 String exchange = returnedMessage.getExchange(); //路由key String routingKey = returnedMessage.getRoutingKey(); System.out.println("消息从交换机到队列失败,详细信息如下:"); System.out.println("消息对象:"+message); System.out.println("错误码:"+replyCode); System.out.println("错误信息:"+replyText); System.out.println("交换机:"+exchange); System.out.println("路由key:"+routingKey); }); //设置ConfirmCallback rabbitTemplate.setConfirmCallback(new RabbitTemplate.ConfirmCallback() { /** * @param correlationData 相关配置信息 * @param ack 交换机是否成功收到消息 * @param cause 错误信息 */ @Override public void confirm(CorrelationData correlationData, boolean ack, String cause) { if (ack){ System.out.println("成功发送消息到交换机"); }else { System.out.println("消息发送到交换机失败"); System.out.println("失败原因:"+cause); } } }); rabbitTemplate.convertAndSend(RabbitMQConfig.ITEM_EXCHANGE,"info", "rabbitmq send message ..."); } }
可以分别故意写错交换机名称和路由键进行测试,打印的错误原因是不同的
四、消费者代码
-
编写消息监听器com.example.rabbitmq_consumer.listener;
@Component public class MyListener { @RabbitListener(queues = "item_queue") public void printMsg(String msg) { System.out.println("消费者接收到的消息为:" + msg); } }