springboot 整合 rabbitMQ(2)

springboot 整合 rabbitMQ(1)-CSDN博客

上期说了rabbitMQ的基础用法(普通队列模式)

这期学习一下如何防止消息重复消费和进阶用法(订阅者模式)

目录

重复消费问题

[导致 RabbitMQ 重复消费问题的原因:](#导致 RabbitMQ 重复消费问题的原因:)

解决思路

代码实现(这里在上一期的代码上进行修改)

生产者:

消费者:

订阅者模式:

是什么:

代码实现:

生产者:

消费者: ​

效果:


重复消费问题

导致 RabbitMQ 重复消费问题的原因:

  1. 网络问题: 在分布式系统中,网络通信是不稳定的因素之一。如果生产者发送一条消息到 RabbitMQ 但尚未收到确认(acknowledgment),可能会导致 RabbitMQ 认为消息未被正确处理并重新发送。

  2. 消费者故障: 消费者在处理消息时可能会发生故障,例如应用程序崩溃或因某种原因终止。如果 RabbitMQ 未收到消费者的确认消息,它可能会认为消息未被消费并重新发送。

  3. 网络分区: 当分布式系统中的网络发生分区(网络隔离)时,可能会导致消息在不同部分之间重复传递。这是因为每个分区可能都会独立处理消息。

  4. 消息重复传递策略: RabbitMQ 提供了不同的消息传递策略,例如"至少一次传递"和"最多一次传递"。这些策略可能会导致消息的重复传递,尤其在异常情况下。

  5. 消费者超时设置不当: 如果消费者设置了较长的超时时间,在消费者未确认消息的情况下,RabbitMQ 可能会认为消息未被处理并重新发送。

解决思路

  • 生产者发送消息时携带一个唯一的id
  • 消费者每次消费前先判断一下在redis中是否在id,不存在就消费,消费完之后就把id存储到redis中

代码实现(这里在上一期的代码上进行修改)

生产者:
消费者:
java 复制代码
    @Autowired
    private RedisTemplate redisTemplate;

    private static final String QUEUE_NAME="login_queue";

    @RabbitListener(queuesToDeclare = @Queue(QUEUE_NAME))
    public void test01(User getUser, Message message, Channel channel) throws IOException {
        //获取消息唯一id
        String messageId = message.getMessageProperties().getMessageId();
        long deliveryTag = message.getMessageProperties().getDeliveryTag();
        try {
            //判断消息是否存在
            if(redisTemplate.opsForHash().hasKey(QUEUE_NAME,messageId)){
                //存在表示该消息已被消费,return
                return;
            }
            //将该消息id存入redis 第三个参数随便字符串,不要填对象,没法序列化
            redisTemplate.opsForHash().put(QUEUE_NAME,messageId,"a");

            //发送消息到邮箱
            sendMail(getUser);
            System.out.println("消费成功");
            //手动确认消息  multiple 是一个布尔值,它决定了是否确认多个消息
            //  false:只确认指定的单个消息  true:确认所有未确认的消息,这些消息的deliveryTag 小于或等于指定的 deliveryTag
            channel.basicAck(deliveryTag,false);
        } catch (Exception e) {
            //消息回滚  deliveryTag:指定要拒绝的消息 false:表示只拒绝指定的这条消息 true:表示将消息重新入队,让其他消费者有机会再次处理这条消息。
            channel.basicNack(deliveryTag,false,true);
            System.out.println("消息消费异常");
        }
    }

订阅者模式:

是什么:

简单解释就是,可以将消息发送给不同类型的消费者。做到发布一次,消费多个。下图取自于官方网站(RabbitMQ)的发布/订阅模式的图例

那么要使交换机接受到消息后转发给队列,就需要将队列绑定给交换机,可以写一个配置类,项目启动,自动将队列绑定给交换机

代码实现:

java 复制代码
/**
 * @ClassName QueueConfig
 * @Description 声明队列和交换机 并将队列和交换机绑定
 * @Author
 * @Date 2024/10/9 16:28
 */
@Configuration
public class QueueConfig {
    //订阅模式交换机名称
    public static String EXCHANGENAME="fanout_exchange";
    public static String TESTQUEUE01="test_01";
    public static String TESTQUEUE02="test_02";

    @Bean
    public Queue queue01(){
        return new Queue(TESTQUEUE01);
    }

    @Bean
    public Queue queue02(){
        return new Queue(TESTQUEUE02);
    }

    @Bean
    public FanoutExchange fanoutExchange(){
        return new FanoutExchange(EXCHANGENAME);
    }

    @Bean
    public Binding bingingQueue01(Queue queue01,FanoutExchange fanoutExchange){
        return BindingBuilder.bind(queue01).to(fanoutExchange);
    }

    @Bean
    public Binding bingingQueue02(Queue queue02,FanoutExchange fanoutExchange){
        return BindingBuilder.bind(queue02).to(fanoutExchange);
    }
}
生产者:

只需要交换机的名字和传入的数据,队列名不需要填,因为交换机已经绑定队列

消费者:
效果:

发送一条数据,两个消费者消费

相关推荐
小林想被监督学习29 分钟前
RabbitMQ 的7种工作模式
分布式·rabbitmq
黑胡子大叔的小屋3 小时前
基于springboot的海洋知识服务平台的设计与实现
java·spring boot·毕业设计
计算机毕设孵化场3 小时前
计算机毕设-基于springboot的校园社交平台的设计与实现(附源码+lw+ppt+开题报告)
spring boot·课程设计·计算机毕设论文·计算机毕设ppt·计算机毕业设计选题推荐·计算机选题推荐·校园社交平台
苹果醋34 小时前
Golang的文件加密工具
运维·vue.js·spring boot·nginx·课程设计
小马爱打代码6 小时前
Spring Boot 中 Map 的最佳实践
java·spring boot·spring
全栈开发帅帅6 小时前
基于springboot+vue实现的博物馆游客预约系统 (源码+L文+ppt)4-127
java·spring boot·后端
m0_748255657 小时前
Springboot基于Web的景区疫情预警系统设计与实现5170q(程序+源码+数据库+调试部署+开发环境)
前端·数据库·spring boot
平行线也会相交8 小时前
云图库平台(三)——后端用户模块开发
数据库·spring boot·mysql·云图库平台
lxyzcm8 小时前
深入理解C++23的Deducing this特性(上):基础概念与语法详解
开发语言·c++·spring boot·设计模式·c++23
wy02_8 小时前
Linux下载RabbitMQ,并解决Github拒绝访问443的问题
linux·rabbitmq·github