【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.rhkb.cn/news/279790.html

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

相关文章

部署应用到K8s集群(未完)

&#xff08;等熟悉一番再来写&#xff0c;因为按小时结算的。。。&#xff09; 1 、 kubectl run 启动 nginx 应用 kubectl run nginx --imagenginx:latest 2、将本地机器的80端口转发到集群中名为nginx的Pod的80端口 kubectl port-forward --address 0.0.0.0 pod/nginx 80:8…

最新ChatGPT/GPT4科研应用与AI绘图及论文高效写作教程

原文链接&#xff1a;最新ChatGPT/GPT4科研应用与AI绘图及论文高效写作教程https://mp.weixin.qq.com/s?__bizMzUzNTczMDMxMg&mid2247598050&idx5&sn70fd3f5946d581ad9c1363295b130ef5&chksmfa823e05cdf5b713baf9cf1381bfb2455ad675a0b21e194bef8b76f35d6aa77…

milvus安装

milvus安装 sudo curl -L “https://github.com/docker/compose/releases/download/1.29.2/docker-compose- $ (uname -s)- $ (uname -m)” -o /usr/local/bin/docker-compose sudo chmod x /usr/local/bin/docker-compose sudo ln -s /usr/local/bin/docker-compose /usr/bin/…

Mysql数据库概念与安装

目录 一、数据库概述 1、数据库的基本概念 2、数据库管理系统&#xff08;DBMS&#xff09; 2.1 数据库管理系统概念 2.2 数据库管理系统工作模式 3、数据库系统&#xff08;DBS&#xff09; 3.1 数据库系统概念 3.2 数据库系统发展史 4、关系型数据库与非关系型数据库…

【刷题】滑动窗口入门

送给大家一句话&#xff1a; 那脑袋里的智慧&#xff0c;就像打火石里的火花一样&#xff0c;不去打它是不肯出来的。——莎士比亚 滑动窗口入门 认识滑动窗口Leetcode 209. 长度最小的子数组题目描述算法思路 Leetcode 3. 无重复字符的最长子串题目描述算法思路 Leetcode 1004…

【Qt学习笔记】(六)界面优化

界面优化 1 QSS1.1 背景介绍1.2 基本语法1.3 QSS设置方式1.3.1 指定控件样式设计1.3.2 全局样式设置1.3.3 使用 Qt Designer 编辑样式 1.4 选择器1.4.1选择器概况1.4.2 子控件选择器&#xff08;Sub-Controls&#xff09;1.4.3伪类选择器(Pseudo-States) 1.5 样式属性1.5.1 盒模…

【计算机网络】https的工作原理以及和http的区别

目录 前言 1. HTTP协议存在的问题 2. 什么是HTTPS协议&#xff1f; 3. HTTP和HTTPS有哪些区别&#xff1f; 4. HTTPS的工作原理 加密方式 前言 在日常的Web项目练习中&#xff0c;我们会发现老师会让我们在打开服务器之后使用 http://localhost/...进行项目效果测试和预览…

Android:adb命令

执行adb命令的窗口如下 Mac或Linux系统里的终端窗口&#xff1b; window系统运行输入cmd打开的指令窗口&#xff1b; Android Studio 里控制下面的Terminal窗口 1. 查看已链接的设备和模拟器 adb devices -l 2. 查看Android内核版本号 adb shell getprop ro.build.version.re…

VMware 配置虚拟机网络

之前需要完成的任务 &#xff08;1&#xff09;、下载和安装VMware-Workstation-Pro.exe软件&#xff0c;推荐16.0版本 &#xff08;2&#xff09;、下载centOS7镜像&#xff0c;可以在阿里云下载。 &#xff08;3&#xff09;、VM创建一个虚拟机&#xff0c;并且使用本地已下载…

【四 (5)数据可视化之 Pyecharts常用图表及代码实现 】

目录 文章导航一、介绍[✨ 特性]二、安装Pyecharts三、主题风格四、占比类图表1、饼图2、环形图3、玫瑰图4、玫瑰图-多图5、堆叠条形图6、百分比堆叠条形图 五、比较排序类1、条形图2、雷达图3、词云图4、漏斗图 六、趋势类图表1、折线图2、堆叠折线图3、面积图4、堆叠面积图 七…

智慧城市新篇章:数字孪生的力量与未来

随着信息技术的迅猛发展和数字化浪潮的推进&#xff0c;智慧城市作为现代城市发展的新模式&#xff0c;正在逐步改变我们的生活方式和社会结构。在智慧城市的构建中&#xff0c;数字孪生技术以其独特的优势&#xff0c;为城市的规划、管理、服务等方面带来了革命性的变革。本文…

Ubuntu虚拟机的IP总频繁变化,导致Xshell断开连接

文章目录 一、IP变化的原因二、解决方法&#xff1a;固定IP三、参考文章 一、IP变化的原因 1.DHCP协议 虚拟机系统(Ubuntu、CentOS、UOS等Linux系统)启动后&#xff0c;加入本地局域网网络时&#xff0c;会向本地网络申请租约一个IP地址&#xff0c;租约时长不定。我这里租约时…

Python之进程池、阻塞模式、非阻塞模式、进程间的通信、queue

非阻塞模式 # 当需要创建的子进程数量不多时&#xff0c;可以直接利用multiprocessing中的Process动态成生多个进程 # 但如果是上百甚至上千个目标&#xff0c;手动的去创建进程的工作量巨大&#xff0c;此时就可以用到multiprocessing模块提供的Pool方法. # 初始化Poo1时&…

程序员下班以后做什么副业合适?

我就是一个最普通的网络安全工程师&#xff0c;出道快10年了&#xff0c;不出意外地遭遇到瓶颈期&#xff0c;但是凭技术在各大平台挖漏洞副业&#xff0c;硬是妥妥扛过来了。 因为对于程序员来讲&#xff0c;这是个试错成本很低、事半功倍的选择。编程技能是一种强大生产力&a…

Flutter-底部弹出框(Widget层级)

需求 支持底部弹出对话框。支持手势滑动关闭。支持在widget中嵌入引用。支持底部弹出框弹出后不影响其他操作。支持弹出框中内容固定头部和下面列表时&#xff0c;支持触摸头部并在列表不在头部的时候支持滑动关闭 简述 通过上面的需求可知&#xff0c;就是在界面中可以支持…

行业回暖?这个行业岗位需求飙升6倍!程序员们提前恭喜了!

前言 随着今年史上最长春节假期正式收官&#xff0c;各行各业相继进入开工节奏&#xff0c;就业市场开启持续升温模式。 今年开工首周&#xff0c;人才需求增长明显&#xff0c;求职者活跃度大大增多&#xff0c;就业市场进入了繁忙有序的节奏&#xff0c;呈现出春招市场的勃…

CI/CD实战-git工具使用 1

版本控制系统 本地版本控制系统 集中化的版本控制系统 分布式版本控制系统 git官网文档&#xff1a;https://git-scm.com/book/zh/v2 Git 有三种状态&#xff1a;已提交&#xff08;committed&#xff09;、已修改&#xff08;modified&#xff09; 和 已暂存&#xff08;sta…

查找众数及中位数 - 华为OD统一考试(C卷)

OD统一考试(C卷) 分值: 100分 题解: Java / Python / C++ 题目描述 众数是指一组数据中出现次数量多的那个数,众数可以是多个。 中位数只是指把一组数据从小到大排列,最中间的那个数,如果这组数据的个数是奇数,那最中间那个就是中位数,如果这组数据的个数为偶数,那…

harmonyOS简介及背景

harmonyOS的场景模式18n: 1&#xff08;入口手机&#xff09;8&#xff08;电脑、VR、手环、iPad、智慧屏、&#xff09;–wifi—n(车载、智能家居等所有)harmonyOS不需要考虑软硬件的差异&#xff0c;是一个兼容N种的超级终端harmonyOS干了两件事&#xff1a; &#xff08;1&a…

没有经验就开通抖店,你会遇到以下这些问题!2024抖店教程(新版)

我是王路飞。 没有经验的人去做抖店的话&#xff0c;都会遇到哪些问题呢&#xff1f; 大概率逃脱不开这些问题&#xff1a; 店铺的类型怎么选&#xff1f; 店铺的流量从哪来&#xff1f; 没有货源但又担心做无货源模式会被平台判定违规&#xff1b; 怎么才能快速把店铺做…