MQ四大消费问题一锅端:消息不丢失 + 消息积压 + 重复消费 + 消费顺序性

RabbitMQ-如何保证消息不丢失

  • 生产者把消息发送到 RabbitMQ Server 的过程中丢失

    • 从生产者发送消息的角度来说,RabbitMQ 提供了一个 Confirm(消息确认)机制,生产者发送消息到 Server 端以后,如果消息处理成功,Server 端会返回一个 ack 消息。客户端可以根据消息的处理结果来决定是否要做消息的重新发送,从而确保消息一定到达 RabbitMQ Server 上。

  • RabbitMQ Server 收到消息后在持久化之前宕机,数据未到达队列导致数据丢失

    • 在发布消息时,你可以设置消息的持久化属性,使其在 RabbitMQ 服务器宕机后仍然保存在磁盘上。这样即使服务器宕机,消息仍然会被保存,并在服务器重新启动后恢复。使用事务来确保消息的持久化和发送的原子性。在开启事务模式后,RabbitMQ 会等待消息被完全写入磁盘后才确认发布成功

  • 队列中的数据还未被消费就宕机,队列中的数据消息丢失

    • 在创建队列时,可以将队列设置为持久化,这样在 RabbitMQ 服务宕机时,所有未被消费的消息也将被持久化保存在磁盘中,并在服务恢复时重新加载到队列中。

  • 消费端收到消息还没来得及处理宕机,导致 RabbitMQ Server 认为这个消息已签收

    • 把消息的自动确认机制修改成手动确认,也就是说消费端只有手动调用消息确认方法才表示消息已经被签收。确保消息被成功的业务调用后才发起确认信号来删除队列中的消息。


MQ百万消息持续积压问题

        消息积压的原因是生产者的消息生产速度大于消费者的消费速度,遇到这个问题的时候,需要排查具体的原因再提出解决方案

  • 流量变大,而RabbitMQ服务器配置偏低,导致消息产生速度大于消费速度;

    • 扩容即可。可以纵向扩容,即增加服务器资源,该加内存加内存,该加CPU加CPU。如果纵向扩容不方便,那就横向扩容,即将单机改为集群模式,增加集群节点,并且增加消费者数量,让消费速度快起来!原来是5个消费者,现在变成50个消费者!

    • 通过异步的方式来处理消息、或者通过批量处理的方式来消费。

    • 采用惰性队列扩大交换器和消费者之间的消息可积压空间,正常队列把消息存放在内存中,可利用空间较小,惰性队列接收到消息后直接存入磁盘而非内存,要消费消息时才会从磁盘中读取并加载到内存,支持数百万数据的存储

  • 消费者故障,从而消息只增不减;

    • 通过查看日志搞清楚为什么消费者会故障,据我多年经验,发生此类问题大概率是程序代码写的不够完美,跑着跑着导致内存溢出,然后消费者进程被杀。要想永久解决此问题,需要结合日志分析程序代码,优化代码。临时解决方法是写监控脚本,如果发现消费者进程中断,需要重启服务!

  • 程序逻辑设计有问题,导致生产者持续生产消息,而消费者不消费或者消费慢;

    • 种情况发生的概率其实并不高,总之就是程序逻辑问题,判断的方法也很简单,持续观察服务器的资源耗费情况,如果内存、CPU一切都正常,但就是队列持续增长,而消费速度非常慢。此时,就需要好好查查程序代码了。当然,可以尝试增加消费者数量,看看是否有好转。

业务考虑

        相信,当我们发现消息积压时,想必问题已经比较严重了,或者说已经影响到业务正常运转了,那么当务之急肯定是需要先将业务恢复正常。对于上面第二种情况,直接重启相关服务,让消费者恢复正常,定是首当其冲。

        除此之外,还有一种“断尾求生”的骚操作,就是新开一个队列,将新产生的消息到新队列里,消费者也到新队列里消费。而老的队列,则需要做一个异步处理,慢慢消费掉即可。

当然,如果积压的消息不怎么重要,可有可无的话,那干脆直接删除掉,这样大家都省事不是。


RabbitMQ消息的重复消费问题

消息去重:在消费者端对接收到的消息进行去重操作,可以通过维护一个消息ID的集合(去重表)或者使用唯一标识(查数据库)来判断是否已经消费过该消息。如果已经消费过,则不进行处理,避免重复消费。

消息确认机制:RabbitMQ提供了消息确认机制,可以确保消息被正确地消费。消费者在消费完消息之后,可以手动向RabbitMQ发送确认消息,告知RabbitMQ该消息已经成功消费,RabbitMQ会将该消息标记为已确认,然后删除消息队列中的该消息。


RabbitMQ消息的消费顺序性问题

        业务中可能会存在多个消息需要顺序处理的情况,比如生成订单和扣减库存消息,那肯定先执行生成订单的操作,再执行扣减库存操作。

        但我们的项目一般都是集群部署的,一个队列就会有多个消费者,怎么实现一个队列中所有顺序消息只能有一个消费者消费呢?

        RabbitMQ本身并不能直接保证消息的消费顺序,因为其是基于 AMQP 协议 的,它使用多个消费者在多个线程上并行地消费消息。但是可以采取以下方法间接实现消息的有序消费:

在这里插入图片描述

解决方法

  1. 单消费者对单队列:可以给 RabbitMQ 创建多个queue, 每个消费者只消费一个queue, 生产者根据订单号,把订单号相同的消息放入一个同一个queue。这样同一个订单号的消息就只会被同一个消费者顺序消费。

  2. 使用多个队列和分区键:可以将相同类型的消息发送到多个队列,并使用分区键 routingKey(例如基于消息的某个属性)决定消息要发送到哪个队列。然后,在消费者端,针对每个队列都使用单个消费者来保证消息的消费顺序。

  3. 消费者内部排序:为了减少网络延迟、消费者运行速度等影响,在消费者端,可以对接收到的消息进行排序,以确保按照特定的顺序进行处理。消费者可以在内部维护一个消息缓冲区,按照某个属性或序列号对消息进行排序后再进行处理。

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

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

相关文章

MySQL 人脸向量,欧几里得距离相似查询

前言 如标题,就是通过提取的人脸特征向量,写一个欧几里得 SQL 语句,查询数据库里相似度排前 TOP_K 个的数据记录。做法虽然另类,业务层市面上有现成的面部检索 API,技术层现在有向量数据库。 用 MySQL 关系型存储 128 …

C语言 每日一题 牛客网 11.13 Day17

找零 Z国的货币系统包含面值1元、4元、16元、64元共计4种硬币&#xff0c;以及面值1024元的纸币。 现在小Y使用1024元的纸币购买了一件价值为N(0 < N≤1024)的商品&#xff0c;请问最少他会收到多少硬币&#xff1f; 思路 运用if语句进行判断分类 代码实现 int main() {…

执行力太差的人,如何才能提高执行力?

执行力是计划的落地执行&#xff0c;是按照计划稳步推进&#xff0c;导向结果的能力。不同的人&#xff0c;其执行力有很大的差别。比如说有拖延症的人&#xff0c;基本上是谈不上执行力的&#xff0c;执行力是一个综合体&#xff0c;是多个要素的共同作用。 在企业HR人才测评…

【左程云算法全讲10】打表技巧和矩阵处理技巧

系列综述&#xff1a; &#x1f49e;目的&#xff1a;本系列是个人整理为了秋招面试的&#xff0c;整理期间苛求每个知识点&#xff0c;平衡理解简易度与深入程度。 &#x1f970;来源&#xff1a;材料主要源于左程云算法课程进行的&#xff0c;每个知识点的修正和深入主要参考…

Java进阶(垃圾回收GC)——理论篇:JVM内存模型 垃圾回收定位清除算法 JVM中的垃圾回收器

前言 JVM作为Java进阶的知识&#xff0c;是需要Java程序员不断深度和理解的。 本篇博客介绍JVM的内存模型&#xff0c;对比了1.7和1.8的内存模型的变化&#xff1b;介绍了垃圾回收的语言发展&#xff1b;阐述了定位垃圾的方法&#xff0c;引用计数法和可达性分析发以及垃圾清…

使用阿里云服务器学习Docker

首先我这里选择的系统服务器是CentOS 7.9 64位 因为centos系统里面的安装指令是&#xff1a;yum,而非apt-get. yum install docker -y试着建立一个容器&#xff1a; docker run -d -p 80:80 httpd启动docker的守护进程&#xff1a; sudo systemctl start docker 查看Docke…

一张图搞懂什么是BCD8421编码

如图所示 BCD8421编码的意义是 用四位二进制数表达一位的十进制数 因此十进制下的0&#xff5e;9在BCD8421编码下与其二进制表达是一样的 而多位的十进制数 比如说“10” 则需要将它拆分成两个单独的数“1”和“0” 分别用BCD8421编码表示这两个数 十进制“1” -> 0001 十进…

快速掌握队列的基础知识

目录 队列的特点基于链表实现队列用栈实现队列用队列实现栈 队列是一种线性数据结构&#xff0c;它只允许在一边进行插入操作&#xff08;队尾&#xff09;&#xff0c;另一边进行删除操作&#xff08;队头&#xff09;。插入操作称为入队&#xff0c;删除操作称为出队。队列遵…

代理模式-静态动态代理-jdk动态代理-cglib动态代理

代理模式 静态代理 动态代理&#xff1a;jdk动态代理 cglib动态代理 注意 &#xff1a;下面的代码截图 要配合文字去看 我对代码的每一步都做了解释 所以需要配合图片观看提取吗1111https://pan.baidu.com/s/1OxQSwbQ--t5Zvmwzjh1T0A?pwd1111 这里直接把项目文件 及代码 …

分布式数据库Schema 变更 in F1 TiDB

分布式数据库Schema 变更 in F1 & TiDB 【转载】TiDB 源码阅读系列文章&#xff08;十七&#xff09;DDL 源码解析 | PingCAP 上述文章主要叙述了从DDL语句发起到执行的过程&#xff0c;简单介绍了弄一套相同的模式来后台处理数据回填&#xff0c;从而提高DDL的并发度的一…

K8S容器内安装cur/telnet命令(Alpine Linux离线环境安装curl/telnet或其他工具)

背景 需求&#xff1a; 微服务的基础是镜像&#xff0c;通常在最小化的Linux镜像中安装jdk&#xff0c;然后运行编译好的java程序。将镜像运行到K8S上就得到了微服务Pod&#xff0c;Pod通常使用安装K8S时配置的私有网段&#xff0c;与宿主机不同。很多时候需要排查从Pod网段内…

python用pychart库,实现将经纬度信息在地图上显示

python使用pyecharts对给到的经纬度数据进行位置标注&#xff0c;下面是批量更新。给入数据&#xff0c;将地图生成。实验数据在下面附件。 from pyecharts import options as opts from pyecharts.charts import Geo import osfolder_path F:\\GPS file_names os.listdir(f…