热门标签 | HotTags
当前位置:  开发笔记 > 编程语言 > 正文

高频面试题|RabbitMQ如何防止重复消费?

一.前言最近有很多小伙伴开始找工作,在面试时,面试官经常问到一个题目:RabbitMQ如何防止重复消费?有很多小伙伴这个时候都在想&#

. 前言

最近有很多小伙伴开始找工作,在面试时,面试官经常问到一个题目:RabbitMQ如何防止重复消费?

有很多小伙伴这个时候都在想,消息怎么就会重复消费呢???.......

所以他们在面试后就跑来问健哥,针对这个比较高频的题目,健哥就在这里为大家来讲讲MQ防止重复消费的实现方案吧。

. 面试题考点

如果面试官是健哥的话,那么我想考察的,其实就是候选人除了对技术的基本使用之外,再就是在各种实际应用场景中对可能发生问题的实际处理能力。

所以这道题的考点,最起码有两点:

  • 第一是RabbitMQ中消息的重复消费是如何产生的,我们首先要发现问题,,知道问题产生原因;

  • 第二是针对这个重复消费问题的处理方案及机制。


. 解题分析

接下来健哥就根据上述考点,带大家来一起分析这个问题的解题思路。

3.1RabbitMQ消息重复消费产生原因

根据上图,健哥给大家梳理总结出了消息重复消费的产生过程,如下:

  1. 消费方的业务项目从MQ队列中接收数据;

  2. 接着处理业务;

  3. 业务处理成功后,消费方项目给MQ返回ack进行手动确认;

  4. 返回回调执行结果的过程中,因为网络抖动等原因,回调数据时,MQ没有返回成功,所以MQ队列中的数据会再次发给业务项目,造成重复消费。


3.2. RabbitMQ消息重复消费处理方案

针对消息的重复消费问题,健哥根据上图总结的解决思路如下:

  1. 监听器接收MQ队列中的数据;

  2. 利用redis的setnx命令,以消息唯一id为key,以消息内容为value,超时时间设置为10秒,存入redis中;

  3. 如果能够成功存入,说明没有重复消费,则处理业务,处理完业务后返回ack或者nack确认;

  4. 如果存不进去,则说明重复消费,直接返回ack确认的回调信息就可以了。


3.3解决重复消费案例代码

  • 发送方测试代码

    /*** 测试发送* @author 千锋健哥*/
    @SpringBootTest(classes = ProducerApplication.class)
    @RunWith(SpringRunner.class)
    public class TestProducer {@Autowiredprivate RabbitTemplate rabbitTemplate;@Testpublic void contextLoads() throws IOException {//给消息封装一个唯一id对象CorrelationData messageId = new CorrelationData(UUID.randomUUID().toString());//第四个参数: 设置消息唯一idrabbitTemplate.convertAndSend("交换器名字","路由键","千锋健哥测试MQ重复消费处理!!",messageId);}
    }

    package com.qf.rabbitmq.topic;import com.rabbitmq.client.Channel;
    import org.springframework.amqp.core.Message;
    import org.springframework.amqp.rabbit.annotation.RabbitListener;
    import org.springframework.beans.factory.annotation.Autowired;
    import org.springframework.data.redis.core.StringRedisTemplate;
    import org.springframework.stereotype.Component;import java.io.IOException;
    import java.util.concurrent.TimeUnit;/*** @author 千锋健哥*/
    @Component
    public class Consumer {@Autowiredprivate StringRedisTemplate redisTemplate;@RabbitListener(queues = "队列名字")public void getMessage(String msg, Channel channel, Message message) throws IOException {//0. 获取MessageId, 消息唯一idString messageId = (String) message.getMessageProperties().getHeaders().get("spring_returned_message_correlation");//1. 设置key到Redisif(redisTemplate.opsForValue().setIfAbsent(messageId,"0", 10, TimeUnit.SECONDS)) {//2. 消费消息System.out.println("接收到消息:" + msg);//3. 设置key的value为1redisTemplate.opsForValue().set(messageId,"1",10,TimeUnit.SECONDS);//4.  手动ackchannel.basicAck(message.getMessageProperties().getDeliveryTag(),false);}else {//5. 获取Redis中的value即可 如果是1,手动ackif("1".equalsIgnoreCase(redisTemplate.opsForValue().get(messageId))){channel.basicAck(message.getMessageProperties().getDeliveryTag(),false);}}}
    }

    • 接收方测试代码

. 总结

经过上面的分析,最后健哥再给大家总结一下这个问题的完整答案。

  • 问题产生原因:

因为消费方和MQ服务器网络闪断等原因,造成了接收方消费后,返回给MQ服务器一个ack确认消息,结果MQ没有接收到,造成了重复消费。

  • 解决过程:

利用redis的setnx命令,将消费的消息id存入到redis,超时时间设置为10秒,然后再给mq返回ack。消费前要判断redis中是否存在这个消息id,如果不存在说明没有消费过,则正常消费;如果redis中存在这个消息id,则说明重复消费,直接返回ack,不重复执行业务。

以上就是MQ中消息重复消费的产生原因及解决思路和对应案例,现在你知道该怎么解决了吗?

 


推荐阅读
  • 本文详细介绍了Oracle 11g中的创建表空间的方法,以及如何设置客户端和服务端的基本配置,包括用户管理、环境变量配置等。 ... [详细]
  • 本文详细介绍了 Redis 中的主要数据类型,包括 String、Hash、List、Set、ZSet、Geo 和 HyperLogLog,并提供了每种类型的基本操作命令和应用场景。 ... [详细]
  • 二维码的实现与应用
    本文介绍了二维码的基本概念、分类及其优缺点,并详细描述了如何使用Java编程语言结合第三方库(如ZXing和qrcode.jar)来实现二维码的生成与解析。 ... [详细]
  • 本文将详细介绍如何使用Java编程语言生成指定数量的不重复随机数,包括具体的实现方法和代码示例。适合初学者和有一定基础的开发者参考。 ... [详细]
  • 本文介绍如何通过Java代码调用阿里云短信服务API来实现短信验证码的发送功能,包括必要的依赖添加和关键代码示例。 ... [详细]
  • 本文分享了作者在使用LaTeX过程中的几点心得,涵盖了从文档编辑、代码高亮、图形绘制到3D模型展示等多个方面的内容。适合希望深入了解LaTeX高级功能的用户。 ... [详细]
  • 基于SSM框架的在线考试系统:随机组卷功能详解
    本文深入探讨了基于SSM(Spring, Spring MVC, MyBatis)框架构建的在线考试系统中,随机组卷功能的设计与实现方法。 ... [详细]
  • 本文详细介绍了在Luat OS中如何实现C与Lua的混合编程,包括在C环境中运行Lua脚本、封装可被Lua调用的C语言库,以及C与Lua之间的数据交互方法。 ... [详细]
  • td{border:1pxsolid#808080;}参考:和FMX相关的类(表)TFmxObjectIFreeNotification ... [详细]
  • protobuf 使用心得:解析与编码陷阱
    本文记录了一次在广告系统中使用protobuf进行数据交换时遇到的问题及其解决过程。通过这次经历,我们将探讨protobuf的特性和编码机制,帮助开发者避免类似的陷阱。 ... [详细]
  • 本文详细介绍了如何在循环双链表的指定位置插入新元素的方法,包括必要的步骤和代码示例。 ... [详细]
  • 使用QT构建基础串口辅助工具
    本文详细介绍了如何利用QT框架创建一个简易的串口助手应用程序,包括项目的建立、界面设计与编程实现、运行测试以及最终的应用程序打包。 ... [详细]
  • 本文详细介绍了二叉堆的概念及其在Java中的实现方法。二叉堆是一种特殊的完全二叉树,具有堆性质,常用于实现优先队列。 ... [详细]
  • JUC并发编程——线程的基本方法使用
    目录一、线程名称设置和获取二、线程的sleep()三、线程的interrupt四、join()五、yield()六、wait(),notify(),notifyAll( ... [详细]
  • 电商高并发解决方案详解
    本文以京东为例,详细探讨了电商中常见的高并发解决方案,包括多级缓存和Nginx限流技术,旨在帮助读者更好地理解和应用这些技术。 ... [详细]
author-avatar
xzh
这个家伙很懒,什么也没留下!
PHP1.CN | 中国最专业的PHP中文社区 | DevBox开发工具箱 | json解析格式化 |PHP资讯 | PHP教程 | 数据库技术 | 服务器技术 | 前端开发技术 | PHP框架 | 开发工具 | 在线工具
Copyright © 1998 - 2020 PHP1.CN. All Rights Reserved | 京公网安备 11010802041100号 | 京ICP备19059560号-4 | PHP1.CN 第一PHP社区 版权所有