全国旗舰校区

不同学习城市 同样授课品质

北京

深圳

上海

广州

郑州

大连

武汉

成都

西安

杭州

青岛

重庆

长沙

哈尔滨

南京

太原

沈阳

合肥

贵阳

济南

下一个校区
就在你家门口
+
当前位置:首页  >  技术干货

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

发布时间:2022-09-07 09:47:00
发布人:syq

  一. 前言

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

RabbitMQ如何防止重复消费

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

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

  二. 面试题考点

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

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

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

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

  三. 解题分析

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

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

40

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

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

  接着处理业务;

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

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

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

41

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

  监听器接收MQ队列中的数据:

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

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

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

  3.3解决重复消费的案例代码

  发送方测试代码

/**

 * 测试发送

 * @author 千锋

 */

@SpringBootTest(classes = ProducerApplication.class)

@RunWith(SpringRunner.class)

public class TestProducer {

@Autowired

private RabbitTemplate rabbitTemplate;

@Test

public void contextLoads() throws IOException {

//给消息封装一个唯一id对象

CorrelationData messageId = new CorrelationData(UUID.randomUUID().toString());

//第四个参数: 设置消息唯一id

rabbitTemplate.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 {

 

    @Autowired
    private StringRedisTemplate redisTemplate;

 

    @RabbitListener(queues = "队列名字")

    public void getMessage(String msg, Channel channel, Message message) throws IOException {

        //0. 获取MessageId, 消息唯一id

        String messageId = (String) message.getMessageProperties().getHeaders().get("spring_returned_message_correlation");

        //1. 设置key到Redis
        if(redisTemplate.opsForValue().setIfAbsent(messageId,"0", 10, TimeUnit.SECONDS)) {

 

            //2. 消费消息

            System.out.println("接收到消息:" + msg);

 

            //3. 设置key的value为1

            redisTemplate.opsForValue().set(messageId,"1",10,TimeUnit.SECONDS);

 

            //4.  手动ack

       channel.basicAck(message.getMessageProperties().getDeliveryTag(),false);

 

        }else {

            //5. 获取Redis中的value即可 如果是1,手动ack

          if("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中消息重复消费的产生原因及解决思路和对应案例,现在你知道该怎么解决了吗?更多关于“Java培训”的问题,欢迎咨询千锋教育在线名师。千锋已有十余年的培训经验,课程大纲更科学更专业,有针对零基础的就业班,有针对想提升技术的好程序员班,高品质课程助力你实现java程序员梦想。

相关文章

什么是敏捷开发之Scrum框架?

什么是敏捷开发之Scrum框架?

2023-10-14
敏捷中的故事编写工作坊是什么?

敏捷中的故事编写工作坊是什么?

2023-10-14
scrum master的核心竞争力是什么?

scrum master的核心竞争力是什么?

2023-10-14
什么项目适合使用Scrum?

什么项目适合使用Scrum?

2023-10-14

最新文章

常见网络安全面试题:Windows常用的命令有哪些?

常见网络安全面试题:Windows常用的命令有哪些?

2023-10-09
常见网络安全面试题:根据设备告警如何展开排查?

常见网络安全面试题:根据设备告警如何展开排查?

2023-10-09
常见网络安全面试题:mysql加固呢?(数据库加固)

常见网络安全面试题:mysql加固呢?(数据库加固)

2023-10-09
常见网络安全面试题:windows和linux加固?(操作系统加固)

常见网络安全面试题:windows和linux加固?(操作系统加固)

2023-10-09
在线咨询 免费试学 教程领取