• 高频面试题 | 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解决重复消费案例代码

    • 发送方测试代码

      1. /**
      2.  * 测试发送
      3.  * @author 千锋健哥
      4.  */
      5. @SpringBootTest(classes = ProducerApplication.class)
      6. @RunWith(SpringRunner.class)
      7. public class TestProducer {
      8.     @Autowired
      9.     private RabbitTemplate rabbitTemplate;
      10.     @Test
      11.     public void contextLoads() throws IOException {
      12.         //给消息封装一个唯一id对象
      13.         CorrelationData messageId = new CorrelationData(UUID.randomUUID().toString());
      14.         //第四个参数: 设置消息唯一id
      15.         rabbitTemplate.convertAndSend("交换器名字","路由键","千锋健哥测试MQ重复消费处理!!",messageId);
      16.     }
      17. }
      1. package com.qf.rabbitmq.topic;
      2. import com.rabbitmq.client.Channel;
      3. import org.springframework.amqp.core.Message;
      4. import org.springframework.amqp.rabbit.annotation.RabbitListener;
      5. import org.springframework.beans.factory.annotation.Autowired;
      6. import org.springframework.data.redis.core.StringRedisTemplate;
      7. import org.springframework.stereotype.Component;
      8. import java.io.IOException;
      9. import java.util.concurrent.TimeUnit;
      10. /**
      11.  * @author 千锋健哥
      12.  */
      13. @Component
      14. public class Consumer {
      15.     @Autowired
      16.     private StringRedisTemplate redisTemplate;
      17.     @RabbitListener(queues = "队列名字")
      18.     public void getMessage(String msg, Channel channel, Message message) throws IOException {
      19.         //0. 获取MessageId, 消息唯一id
      20.         String messageId = (String) message.getMessageProperties().getHeaders().get("spring_returned_message_correlation");
      21.         //1. 设置key到Redis
      22.         if(redisTemplate.opsForValue().setIfAbsent(messageId,"0"10, TimeUnit.SECONDS)) {
      23.             //2. 消费消息
      24.             System.out.println("接收到消息:" + msg);
      25.             //3. 设置keyvalue1
      26.             redisTemplate.opsForValue().set(messageId,"1",10,TimeUnit.SECONDS);
      27.             //4.  手动ack
      28.        channel.basicAck(message.getMessageProperties().getDeliveryTag(),false);
      29.         }else {
      30.             //5. 获取Redis中的value即可 如果是1,手动ack
      31.           if("1".equalsIgnoreCase(redisTemplate.opsForValue().get(messageId))){
      32.                 channel.basicAck(message.getMessageProperties().getDeliveryTag(),false);
      33.             }
      34.         }
      35.     }
      36. }
      • 接收方测试代码

    . 总结

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

    • 问题产生原因

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

    • 解决过程:

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

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

     *威哥Java学习交流Q群:691533824
    加群备注:CSDN推荐
          

  • 相关阅读:
    vuepress-plugin-comment -use Valine
    酒店订房退房管理系统(数组应用)
    mysql 分库分表
    【MySQL】MySQL中出现错误代码: 1052 Column ‘xxx‘ in field list is ambiguous的原因和解决方法
    在iframe标签中操作外层dom
    【读书笔记】【Effective C++】模板与泛型编程
    Matlab 矩阵基础
    ios照片误删怎么恢复,iphone已经删除的照片怎么恢复
    骚操作之 持有 ReadOnlySpan 数据
    谈谈JMM
  • 原文地址:https://blog.csdn.net/finally_vince/article/details/126782674