【RabbitMQ | 第六篇】消息重复消费问题及解决方案

在这里插入图片描述

文章目录

  • 6.消息重复消费问题
    • 6.1问题介绍
    • 6.2解决思路
    • 6.3将该消息存储到Redis
      • 6.3.1将id存入string(单消费者场景)
        • (1)实现思路
        • (2)问题
      • 6.3.2将id存入list中(多消费场景)
        • (1)实现思路
      • 6.3.3将id以key增量存入string中并设置过期时间
        • (1)实现思路
    • 6.4总结

6.消息重复消费问题

6.1问题介绍

什么是消息重复消费?首先我们来看一下消息的传输流程。消息生产者–>MQ–>消息消费者;消息生产者发送消息到MQ服务器,MQ服务器存储消息,消息消费者监听MQ的消息,发现有消息就消费消息。

所以消息重复也就出现在 两个阶段

1 :生产者多发送了消息给MQ;

2 :MQ的一条消息被消费者消费了多次

具体场景如下:

  1. 生产者发送消息给MQ在MQ确认的时候出现了网络波动,生产者没有收到确认,这时候生产者就会重新发送这条消息,导致MQ会接收到重复消息。
  2. 消费者消费成功后,给MQ确认的时候出现了网络波动,MQ没有接收到确认,为了保证消息不丢失,MQ就会继续给消费者投递之前的消息。这时候消费者就接收到了两条一样的消息。由于重复消息是由于网络原因造成的,无法避免。

6.2解决思路

  1. 发送消息时让每个消息携带一个全局的唯一ID
  2. 在消费消息时先判断消息是否已经被消费过,保证消息消费逻辑的幂等性。具体消费过程为:
    • 消费者获取到消息后先根据id去查询redis/db是否存在该消息
    • 如果不存在,则正常消费,消费完毕后写入redis/db
    • 如果存在,则证明消息被消费过,直接丢弃

6.3将该消息存储到Redis

6.3.1将id存入string(单消费者场景)

(1)实现思路
  • 将id号存入value中,并且value类型为string
  • 即以队列名称为key,以消息id为值
  • 每次消息过来都覆盖之前的消息
    @RabbitListener(queues = "queueName4")//发送的队列名称     @RabbitListener注解到类和方法都可以@RabbitHandlerpublic void receiveMessage1(Message message) throws UnsupportedEncodingException {//获取唯一idString messageId = message.getMessageProperties().getMessageId();String msg = new String(message.getBody(),"utf-8");//获取redis中该队列名称对应的value值String messageRedisValue = redisUtil.get("queueName4","");//检验唯一id是否存在if (messageRedisValue.equals(messageId)) {//存在return;}System.out.println("消息:"+msg+", id:"+messageId);//以队列为key,id为valueredisUtil.set("queueName4",messageId);}
(2)问题
  1. 并发冲突:如果多个消费者同时操作 Redis 中的已消费消息列表,由于 Redis 是单线程处理命令,可能会出现并发冲突导致数据不一致或丢失问题。特别是在高并发情况下,使用字符串类型的 ID 可能会增加并发冲突的风险
  2. 内存占用:字符串类型的 ID 在内存中占用空间相对较大,尤其是对于大量消息的情况下,会增加 Redis 的内存占用。
  3. 比较效率:字符串类型的 ID 比较起来相对复杂,需要进行字符串比较操作。

6.3.2将id存入list中(多消费场景)

(1)实现思路
  • 以该队列名称为key,id为value
  • 适合多消费场景的原因:
    • 顺序性:List 是一个有序集合,可以按照消息的顺序存储消息 ID。在多消费者场景下,保持消息的顺序通常是很重要的,以确保消息按照正确的顺序被消费。
    • 原子性操作:Redis 的 List 提供了多个原子性操作,比如从列表两端推入/弹出元素,这些操作可以确保多个消费者同时访问列表时不会出现数据竞争和并发问题。
    • 支持阻塞操作:List 提供了阻塞式的弹出操作(如 BLPOP、BRPOP),可以在没有消息时阻塞等待新消息的到来,这对于实现消费者轮询机制非常有用。
@RabbitListener(queues = "queueName4")//发送的队列名称     @RabbitListener注解到类和方法都可以
@RabbitHandler
public void receiveMessage2(Message message) throws UnsupportedEncodingException {String messageId = message.getMessageProperties().getMessageId();String msg = new String(message.getBody(),"utf-8");//获取List<String> messageRedisValue = redisUtil.lrange("queueName4");if (messageRedisValue.contains(messageId)) {return;}System.out.println("消息:"+msg+", id:"+messageId);redisUtil.lpush("queueName4",messageId);//存入list
}

6.3.3将id以key增量存入string中并设置过期时间

(1)实现思路

消息id为key消息内容为value存入string中,设置过期时间( 可承受的redis服务器异常时间,比如设置过期时间为10分钟,如果redis服务器断了20分钟,那么未消费的数据都会丢了)

    @RabbitListener(queues = "queueName4")//发送的队列名称     @RabbitListener注解到类和方法都可以@RabbitHandlerpublic void receiveMessage2(Message message) throws UnsupportedEncodingException {String messageId = message.getMessageProperties().getMessageId();String msg = new String(message.getBody(),"utf-8");String messageRedisValue = redisUtil.get(messageId,"");if (msg.equals(messageRedisValue)) {return;}System.out.println("消息:"+msg+", id:"+messageId);//以id为key,消息内容为value,过期时间10分钟redisUtil.set(messageId,msg,10L);}

6.4总结

该篇文章介绍了消息重复消费问题及解决方案,问题可能产生的两个阶段(生产消息多发、消费者重复消息);解决方案:将消息发送时携带一个唯一id,消费方拿到消息时先去reids/db中有没有该数据,若没有则可以消费,否则不可以消费;并介绍了基于Redsi解决消息重复消费问题,①以队列名称为key,消息id为value,且value为string类型(适合只有一个消费方)②以队列名称为key,消息id为value,且value为list类型(适合有多个消费方场景)③以消息id为key,内容为value,并设置过期时间

在这里插入图片描述

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/757856.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

首页效果炫酷的wordpress免费主题模板

视频背景免费WP主题 简洁大气的视频背景wordpress主题&#xff0c;找大视频背景的主题可以看看这个。 https://www.wpniu.com/themes/193.html 红色全屏大图WP主题 非常经典的一款免费wordpress主题&#xff0c;红色全屏大图满足多行业使用。 https://www.wpniu.com/themes…

9.登入页面

登入页面 在pages中新建页面login 修改代码 <template><view></view> </template><script setup></script><style lang"scss"></style>添加头像组件 官网 https://vkuviewdoc.fsq.pub/components/avatar.html …

【靶机测试--PHOTOGRAPHER: 1【php提权】】

前期准备 靶机下载地址&#xff1a; https://vulnhub.com/entry/photographer-1%2C519/ 信息收集 nmap 扫描同网段 ┌──(root㉿kali)-[/home/test/桌面] └─# nmap -sP 192.168.47.0/24 --min-rate 3333 Starting Nmap 7.92 ( https://nmap.org ) at 2024-03-19 07:37 …

SpringCloud Alibaba Nacos 服务注册和配置中心

一、前言 接下来是开展一系列的 SpringCloud 的学习之旅&#xff0c;从传统的模块之间调用&#xff0c;一步步的升级为 SpringCloud 模块之间的调用&#xff0c;此篇文章为第十二篇&#xff0c;即介绍 SpringCloud Alibaba Nacos 服务注册和配置中心。 二、Nacos 简介 2.1 为…

SpringBoot 监控 SQL 运行情况

Druid 数据库连接池相信很多小伙伴都用过,个人感觉 Druid 是阿里比较成功的开源项目了,不像 Fastjson 那么多槽点,Druid 各方面一直都比较出色,功能齐全,使用也方便,基本的用法就不说了,今天我们来看看 Druid 中的监控功能。 准备工作 首先我们来创建一个 Spring Boot…

wireshark数据捕获实验简述

Wireshark是一款开源的网络协议分析工具&#xff0c;它可以用于捕获和分析网络数据包。是一款很受欢迎的“网络显微镜”。 实验拓扑图&#xff1a; 实验基础配置&#xff1a; 服务器&#xff1a; ip:172.16.1.88 mask:255.255.255.0 r1: sys sysname r1 undo info enable in…

YOLOv5目标检测学习(6):源码解析之:训练部分train.py

文章目录 前言一、导入相关包与配置二、主函数main2.1 checks&#xff1a;检查rank值来判断是否打印参数、检查git仓库、检查包的安装2.2 判断是否恢复上一次模型训练提问&#xff1a;opt.data, opt.cfg, opt.hyp, opt.weights, opt.project各是什么&#xff1f; 2.3 DDP mode&…

【数据结构】哈希表与哈希桶

&#x1f440;樊梓慕&#xff1a;个人主页 &#x1f3a5;个人专栏&#xff1a;《C语言》《数据结构》《蓝桥杯试题》《LeetCode刷题笔记》《实训项目》《C》《Linux》《算法》 &#x1f31d;每一个不曾起舞的日子&#xff0c;都是对生命的辜负 目录 前言 1.概念 2.哈希冲突…

mysql查询条件包含IS NULL、IS NOT NULL、!=、like %* 、like %*%,不能使用索引查询,只能使用全表扫描,是真的吗???

不知道是啥原因也不知道啥时候, 江湖上流传着这么一个说法 mysql查询条件包含IS NULL、IS NOT NULL、!、like %* 、like %*%,不能使用索引查询&#xff0c;只能使用全表扫描。 刚入行时我也是这么认为的&#xff0c;还奉为真理&#xff01; 但是时间工作中你会发现还是走索引…

图书推荐|图解算法:C语言实现+视频教学版

零负担理解数据结构及其算法的设计&#xff0c;零基础也能快速上手编程。 本书内容 《图解算法&#xff1a;C语言实现视频教学版》是一本综合讲述数据结构及其算法的入门书&#xff0c;力求简洁、清晰、严谨、且易于学习和掌握。 《图解算法&#xff1a;C语言实现视频教学版》…

经过拦截器cookie验证后用户信息放入哪里,以便后面接口使用

1.基于ThreadLocal实现&#xff0c;拦截器获取的用户信息在接口中使用_threadloacl获取账号数据_JAVA&#xff24;WangJing的博客-CSDN博客2基于ThreadLocal实现&#xff0c;拦截器获取的用户信息在接口中使用_threadloacl获取账号数据https://blog.csdn.net/wang_jing_jing/ar…

07 - Debian如何分区、格式化与挂载磁盘

作者&#xff1a;网络傅老师 特别提示&#xff1a;未经作者允许&#xff0c;不得转载任何内容。违者必究&#xff01; Debian如何分区、格式化与挂载磁盘 《傅老师Debian小知识库系列之07》——原创 前言 傅老师Debian小知识库特点&#xff1a; 1、最小化拆解Debian实用技能…

echarts多个折线图共用一个x轴和tooltip组件

实现效果 根据接口传来的数据&#xff0c;使用echarts绘制出&#xff0c;共用一个x轴的图表 功能&#xff1a;后端将所有数据传送过来&#xff0c;前端通过监听选中值来展示对应的图表数据 数据格式&#xff1a; 代码&#xff1a; <template><div><div clas…

Knife4j的相关知识点!!

一、基础概念 knife4j是为Java MVC框架集成Swagger生成Api文档的增强解决方案,前身是swagger-bootstrap-ui,取名kni4j是希望它能像一把匕首一样小巧,轻量,并且功能强悍! Knif4j&#xff08;原名为 Swagger-Bootstrap-UI&#xff09;是一款基于 Swagger 实现的文档管理工具&am…

3D模型网站怎么选择靠谱?

3D模型在各个领域的应用越来越广泛&#xff0c;如设计、建筑等。而在网上寻找高质量的3D模型成为了许多设计师和创作者的迫切需求。然而&#xff0c;面对众多的3D模型网站&#xff0c;如何选择一个靠谱的3D模型网站呢? 选择靠谱的3D模型网站是一个需要综合考虑多方面因素的过程…

图神经网络实战(5)——常用图数据集

图神经网络实战&#xff08;5&#xff09;——常用图数据集 0. 前言0. 图数据集介绍2. Cora 数据集3. Facebook Page-Page 数据集小结系列链接 0. 前言 图数据集往往比单纯的连接集合更丰富&#xff0c;节点和边也可以具有表示分数、颜色、单词等的特征。在输入数据中包含这些…

Golang 开发实战day04 - Standard Library

Golang 开发实战day04 - Standard Library 接下来开始我们第四天学习&#xff0c;Go语言标准库提供了丰富的功能&#xff0c;可以帮助开发者快速完成各种任务。 golang就像其他语言一样&#xff0c;附带了一些非常轻量级的函数和特性&#xff0c;都是开箱即用的&#xff0c;这里…

Linux 管道

目录 一、认识管道 二、匿名管道 pipe函数 用法&#xff1a; pipefd&#xff1a; 匿名管道通信&#xff1a; 三、命名管道 概念&#xff1a; 创建&#xff1a; 特性&#xff1a; 用途&#xff1a; 四、命名管道和匿名管道的区别 命名&#xff1a; 持久性&#xff1a;…

改进YOLOv8注意力系列六:结合SEAttention轻量通道注意力、ShuffleAttention重排特征注意力模块、SimAM无参数化注意力

改进YOLOv8注意力系列五:结合ParNetAttention注意力、高效的金字塔切分注意力模块PSA、跨领域基于多层感知器(MLP)S2Attention注意力 代码SEAttention轻量通道注意力ShuffleAttention重排特征注意力模块SimAM无参数化注意力加入方法各种yaml加入结构本文提供了改进 YOLOv8注…

【PyQt】17-日历控件

文章目录 前言一、代码二、运行结果总结 前言 固定格式的表述 日期的获取 一、代码 #Author &#xff1a;susocool #Creattime:2024/3/19 #FileName:40-日历控件 #Description: 日历控件的展示 import sys from PyQt5.QtCore import * from PyQt5.QtWidgets import * from PyQ…