日常记录-SpringBoot整合RabbitMQ第三节(生产者ConfirmCallback和ReturnCallback)

参考以下博主的文章

RabbitMQ入门 安装 SpringAMQP简单队列、工作队列、发布订阅(扇出模式,广播模式)、Direct模式(Roting模式)、Topic模式

RabbitMQ(2)、MQ问题:消息可靠性、延迟消息( 延迟队列(插件 ))、消息堆积(惰性队列)、MQ的高可用。ConfirmCallback机制、ReturnCallback机制、死信交换机

我这里只会记录如何整合SpringBoot,安装和部署的具体详情可以看上面这位博主写的文章。

一、MQ解决什么问题

解决问题:实现异步、削峰、解耦

常见问题:

消息可靠性问题:如何确保消息被成功送达消费者,并且被消费者成功消费掉

延迟消息问题:如果一个消息,需要延迟15分钟再消费,像12306超时取消订单,如何实现消息的延迟投递
(如果你的消息设置的延迟时间超过MQ的极限,消息会立刻给消费掉的。消息过期时间必须是非负32为整数,即:0<=n<=2^32-1,单位(毫秒) 。2^32-1=4294967295 约等于49天左右)

消息堆积问题:如果消息无法被及时消费而堆积,如何解决百万级消息堆积的问题

扫描二维码关注公众号,回复: 16467466 查看本文章

MQ的高可用问题:如何避免MQ因为单点故障而不可用的问题

二、MQ的问题与解决方案

环节一 :生产者 -> 交换机
环节二 :交换机 -> 队列
环节三 : 队列 ->消费者
如果以上其中一个环节出了问题,消息就无法到达消费者

在这里插入图片描述

生产者发送消息可能出现的问题:

  1. 生产者发送的消息未送达交换机
  2. 生产者发送的消息到达交换机,但未到达队列

MQ收到消息后丢失的问题:

  1. MQ宕机,导致未持久化保存消息丢失
  2. 消费者接收消息后,尚未消费MQ就宕机

MQ对应的解决方案有以下:

  1. 生产者发送消息丢失:使用生产者确认机制
  2. MQ接收消息丢失:MQ消息持久化
  3. 消费者接收消息丢失:消费者确认机制与失败重试机制

三、生产者确认

生产者 -> 交换机 -> 队列
通过ConfirmCallback和ReturnCallback才能确认消息是否到达队列
在这里插入图片描述

1、ConfirmCallback(确认消息是否到达交换机)

application.yml添加配置publisher-confirm-type配置,确认机制异步还是同步,自己根据业务决定
在这里插入图片描述
模拟生产方确认消息是否到达交换机

import org.junit.jupiter.api.Test;
import org.springframework.amqp.rabbit.connection.CorrelationData;
import org.springframework.amqp.rabbit.core.RabbitTemplate;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;

@SpringBootTest
public class ConfirmCallbackConfig {
    
    


    @Autowired
    private RabbitTemplate rabbitTemplate;

    @Test
    public void test(){
    
    
        //创建一个CorrelationData Correlation:关联
        CorrelationData correlationData = new CorrelationData();
        //设置消息的id
        correlationData.setId("msg-888888");
        //准备好 回调函数:Callback,用于接收 将来:Future 的确认结果
        correlationData.getFuture().addCallback(
                /**
                 * 当消息发送没有出现异常时,这个方法会被调用
                 */
                result -> {
    
    
                    if (result.isAck()){
    
    
                        System.out.println("发送消息成功,消息已到达交换机,消息id:"+correlationData.getId());
                    }else {
    
    
                        System.out.println("发送消息成功,消息没有到达交换机,消息id:"+correlationData.getId()+" 原因:"+result.getReason());

                    }                },
                /**
                 * 当消息发送出现异常时,这个方法会被调用
                 */
                ex -> {
    
    
                    System.out.println("发送消息异常,消息id:"+correlationData.getId()+" 异常:"+ex);
                }
        );
        //自己随便定一个没有的交换机
        rabbitTemplate.convertAndSend("xxxx.exchange", "demo", "到达交换机了吗",correlationData);
    }



}

名称为xxxx.exchange的交换机我没有
在这里插入图片描述
消息没有送达指定交换机
在这里插入图片描述
消息送达指定交换机
在这里插入图片描述在这里插入图片描述

2、ReturnCallback(确认消息是否到达队列)

application.yml新增ReturnCallback的配置,写在生产者这边,我生产者和消费者都是同一个服务

在这里插入图片描述
创建ReturnCallback配置类,统一管理

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 RabbitReturnCallBackConfig implements ApplicationContextAware {
    
    

    @Override
    public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
    
    
        RabbitTemplate rabbitTemplate = applicationContext.getBean(RabbitTemplate.class);
        rabbitTemplate.setReturnCallback((message, replyCode, replyText, exchange, routingKey) -> {
    
    
            //当交换机把消息路由到队列出现问题时,这个方法会自动执行
            log.warn("把消息路由到队列失败,replyCode{},replyText{},RoutingKey={},msg={}", replyCode,replyText,exchange,routingKey, message);
        });
    }


}

定义交换机、队列

import org.springframework.amqp.core.*;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

/**
 * 需要:
 *      1. 声明Direct类型的交换机:direct.exchange
 *      2. 声明队列1:direct.queue1
 *         声明队列2:direct.queue2
 *      3. 把交换机与队列1绑定:把 RoutingKey为orange的消息 -> 投递到队列1
 *         把交换机与队列2绑定:把 RoutingKey为black的消息  -> 投递到队列2
 *         把交换机与队列2绑定:把 RoutingKey为green的消息  -> 投递到队列2
 *
 *                             ↗ RoutingKey = orange   ->  direct.queue1 -> C(消费者1)
 * P(生产者) -> direct.exchange
 *                             ↘ RoutingKey = black
 *                                                    ->  direct.queue2   -> C(消费者2)
 *                             ↘ RoutingKey = green
 *
 */
@Configuration
public class DirectQueueConfig {
    
    


    //定义direct类型交换机
    @Bean
    public DirectExchange directExchange() {
    
    
        return ExchangeBuilder.directExchange("direct.exchange").build();
    }


    //定义持久化队列
    @Bean
    public Queue directQueue1() {
    
    
        return new Queue("direct.queue1",true,false,false);
    }

    //定义持久化队列
    @Bean
    public Queue directQueue2() {
    
    
        return QueueBuilder.durable("direct.queue2").build();
    }

    //把交换机与队列direct.queue1绑定:把 RoutingKey为orange的消息,投递到队列direct.queue1
    @Bean
    public Binding directQueue1Binding(Queue directQueue1, DirectExchange directExchange) {
    
    
        return BindingBuilder.bind(directQueue1).to(directExchange).with("orange");
    }

    //把交换机与队列direct.queue2绑定:把 RoutingKey为black的消息,投递到队列direct.queue2
    @Bean
    public Binding directQueue2BindingInfo(Queue directQueue2, DirectExchange directExchange) {
    
    
        return BindingBuilder.bind(directQueue2).to(directExchange).with("black");
    }
    //把交换机与队列direct.queue2绑定:把 RoutingKey为green的消息,投递到队列direct.queue2
    @Bean
    public Binding direcQueue2BindingError(Queue directQueue2, DirectExchange directExchange) {
    
    
        return BindingBuilder.bind(directQueue2).to(directExchange).with("green");
    }



}

模拟生产者

import org.junit.jupiter.api.Test;
import org.springframework.amqp.rabbit.core.RabbitTemplate;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;

@SpringBootTest
public class ReturnCallBackTest {
    
    

    @Autowired
    private RabbitTemplate rabbitTemplate;

    //模拟生产者
    @Test
    public void test(){
    
    
        
        //rabbitTemplate.convertAndSend("direct.exchange","orange","这是orange");
        //rabbitTemplate.convertAndSend("direct.exchange","black","这是black");
        //rabbitTemplate.convertAndSend("direct.exchange","green","这是green");

        //没有绑定routingkey=xxxx的队列
        rabbitTemplate.convertAndSend("direct.exchange","xxxx","这是green");
    }
}

捕获到消息发送失败了
在这里插入图片描述

猜你喜欢

转载自blog.csdn.net/qq407995680/article/details/132086350