RabbitMQ 在实际应用时要注意的问题

1. 幂等性保障

1.1 幂等性介绍

        幂等性是数学和计算机科学中某些运算的性质,它们可以被多次应⽤,⽽不会改变初始应⽤的结果.

应⽤程序的幂等性介绍

        在应⽤程序中,幂等性就是指对⼀个系统进⾏重复调⽤(相同参数),不论请求多少次,这些请求对系统的影响都是相同的效果.

        ⽐如数据库的 select 操作.不同时间两次查询的结果可能不同,但是这个操作是符合幂等性的.幂等性指的是对资源的影响,⽽不是返回结果.查询操作对数据资源本⾝不会产⽣影响,之所以结果不同,可能是因为两次查询之间有其他操作对资源进⾏了修改.

        ⽐如 i++ 这个操作,就是⾮幂等性的.如果调⽤⽅没有控制好逻辑,⼀次流程重复调⽤好⼏次,结果 就会不同.

MQ 的幂等性介绍

        对于 MQ ⽽⾔,幂等性是指同⼀条消息,多次消费,对系统的影响是相同的.⼀般消息中间件的消息传输保障分为三个层级.

1. At most once:最多⼀次.消息可能会丢失,但绝不会重复传输.

2. At least once:最少⼀次.消息绝不会丢失,但可能会重复传输.

3. Exactly once:恰好⼀次.每条消息肯定会被传输⼀次且仅传输⼀次.

        RabbitMQ ⽀持"最多⼀次"和"最少⼀次".对于"恰好⼀次",⽬前 RabbitMQ 还做不到,不仅是RabbitMQ,⽬前市⾯上主流的消息中间件,都做不到 这⼀点.

        在业务使⽤中,对于可靠性要求⽐较⾼的场景,建议使⽤"最少⼀次",以防⽌消息丢失. "最多⼀次"会因为消息发送过程中,⽹络问题,消费出现异常等种种原因,导致消息丢失.

以下场景可能会导致消息发送重复(包含但不限于)

        • 发送时消息重复:当⼀条消息已被成功发送到服务端并完成持久化,此时出现了⽹络闪断或者客户端宕机,导致服务端对客户端应答失败.如果此时 Producer 意识到消息发送失败并尝试再次发送消息, Consumer 后续会收到两条内容相同并且 Message ID 也相同的消息.

        • 投递时消息重复:消息消费的场景下,消息已投递到 Consumer 并完成业务处理,当客户端给服务端反馈应答的时候⽹络闪断.为了保证消息⾄少被消费⼀次,云消息队列 RabbitMQ 版的服务端将在⽹络恢复后再次尝试投递之前已被处理过的消息,Consumer 后续会收到两条内容相同并且 Message ID也相同的消息.

        但是"最少⼀次",就会造成⼀个问题,消费端会收到重复的消息,也会造成对同⼀条消息进⾏多次处理.⼀ 些不重要的

        业务还好⼀点,对于重要的业务,如果不对重复的消息进⾏处理,会造成严重事故.

        ⽐如:当⽤户对⼀个订单付款之后,因为⽹络问题,付款成功的结果未返回给订单系统,当⽤户再次点击付款时,果系统未做幂等性处理,那就会造成两次扣款。

1.2 解决方案

        MQ 消费者的幂等性的解决⽅法,⼀般有以下⼏种:

全局唯⼀ ID

        1. 为每条消息分配⼀个唯⼀标识符,⽐如 UUID 或者 MQ 消息中的唯⼀ID,但是⼀定要保证唯⼀性. 

        2. 消费者收到消息后,先⽤该 id 判断该消息是否已经消费过,如果已经消费过,则放弃处理.

         3. 如果未消费过,消费者开始消费消息,业务处理成功后,把唯⼀ ID 保存起来(数据库或Redis等)

2. 顺序性保障

2.1 顺序性保障介绍

        消息的顺序性是指消费者消费的消息和⽣产者发送消息的顺序是⼀致的.

        ⽐如⽣产者发送的消息分别是 msg1,msg2,msg3,那么消费者也是按照msg1,msg2,msg3的顺序进⾏消费的.

        很多业务场景下,消息的消费是不⽤保证顺序的,⽐如使⽤ MQ 实现订单超时的处理.但有些业务场景, 能存在多个消息顺序处理的情况.⽐如⽤户信息修改,对同⼀个⽤户的同⼀个资料进⾏修改,需要保证消息的顺序      

        ⼀些资料显⽰ RabbitMQ 的消息能够保障顺序性,这是不严谨的.在不考虑消息丢失,⽹络故障等异常的情况下,如果只有⼀个消费者,最好也只有⼀个⽣产者的情况下,是可以保证消息的顺序性.如果有多个⽣产者同时发送消息,⽆法确定消息到达 RabbitMQ Broker 的前后顺序,也就⽆法验证消息的顺序性.哪些情况可能会打破 RabbitMQ 的顺序性呢?下⾯介绍⼏种常⻅的场景:

          1. 多个消费者:当队列配置了多个消费者时,消息可能会被不同的消费者并⾏处理,从⽽导致消息处理的顺序性⽆法保证.

        2. ⽹络波动或异常:在消息传递过程中,如果出现⽹络波动或异常,可能会导致消息确认(ACK)丢失,从⽽使得消息被重新⼊队和重新消费,造成顺序性问题.

        3. 消息重试:如果消费者在处理消息后未能及时发送确认,或者确认消息在传输过程中丢失,那么MQ 可能会认为消息未被成功消费⽽进⾏重试,这也可能导致消息处理的顺序性问题.

        4. 消息路由问题:在复杂的路由场景中,消息可能会根据路由键被发送到不同的队列,从⽽⽆法保证全局的顺序性.

        5. 死信队列:消息因为某些原因(如消费端拒绝消息)被放⼊死信队列,死信队列被消费时,⽆法保证消息的顺序和⽣产者发送消息的顺序⼀致

        包括但不仅限于以上⼏种情形会使 RabbitMQ 消息错序,如果要保证消息的顺序性,需要业务⽅使⽤ RabbitMQ 之后做进⼀步的处理

2.2 顺序性保障方案

        消息顺序性保障分为:局部顺序性保证和全局顺序性保证.

        局部顺序性通常指的是在单个队列内部保证消息的顺序.全局顺序性是指在多个队列或多个消费者之间保证消息的顺序.

        在实际应⽤中,全局顺序性很难实现,可以考虑使⽤业务逻辑来保证顺序性,⽐如在消息中嵌⼊序列号,并在消费端进⾏排序处理.相对⽽⾔,局部顺序性更常⻅,也更容易实现.

        RabbitMQ 作为⼀个分布式消息队列,主要优化的是吞吐量和可⽤性,⽽不是严格的顺序性保证.如果业务场景确实需要严格的消息顺序,可能需要在应⽤层⾯进⾏额外的设计和实现.

接下来说⼀下消息的顺序性保证的常⻅策略.

1. 单队列单消费者

        最简单的⽅法是使⽤单个队列,并由单个消费者进⾏处理.同⼀个队列中的消息是先进先出的,这是 RabbitMQ来帮助我们保证的.

2. 分区消费

        单个消费者的吞吐太低了,当需要多个消费者以提⾼处理速度时,可以使⽤分区消费.把⼀个队列分割成多个分区,每个分区由⼀个消费者处理,以此来保持每个分区内消息的顺序性.

        ⽐如⽤户修改资料后,发送⼀条⽤户资料消息.消费者在处理时,需要保证消息发送的先后顺序,但这种场合并不需要保证全局顺序.只需要保证同⼀个⽤户的消息顺序消费就可以.这时候就可以采 ⽤把消费按照⼀定的规则,分为多个区,每个分区由⼀个消费者处理 RabbitMQ 本⾝并不⽀持分区消费,需要业务逻辑去实现,或者借助 spring-cloud-stream 来实现

参考:https://docs.spring.io/spring-cloud-stream/reference/rabbit/rabbit_partitions.html

3. 消息确认机制

        使⽤⼿动消息确认机制,消费者在处理完⼀条消息后,显式地发送确认,这样 RabbitMQ 才会移除并继续发送下⼀条消息.

4. 业务逻辑控制

        在某些情况下,即使消息乱序到达,也可以在业务逻辑层⾯实现顺序控制.⽐如通过在消息中嵌⼊序列号,并在消费时根据这些信息来处理 RabbitMQ 本⾝并不保证全局的严格顺序性,特别是在分布式系统中.在实际应⽤开发中,根据具体的业 务需求,可能需要结合多种策略来实现所需要的顺序保证.

3. 消息积压问题

3.1 原因分析

        消息积压是指在消息队列(如 RabbitMQ )中,待处理的消息数量超过了消费者处理能⼒,导致消息在队列中不断堆积的现象.

通常有以下⼏种原因:

        1. 消息⽣产过快:在⾼流量或者⾼负载的情况下,⽣产者以极⾼的速率发送消息,超过了消费者的处理能⼒.

        2. 消费者处理能⼒不⾜:消费者处理消息的速度跟不上消息⽣产的速度,也会导致消息在队列中积压.

        可能原因有:

                1)消费端业务逻辑复杂,耗时⻓

                2)消费端代码性能低

                3)系统资源限制,如CPU、内存、磁盘I/O等也会限制消费者处理消息的效率.

                4)异常处理不当.消费者在处理消息时出现异常,导致消息⽆法被正确处理和确认.

3. ⽹络问题:因为⽹络延迟或不稳定,消费者⽆法及时接收或确认消息,最终导致消息积压

4. RabbitMQ 服务器配置偏低

        消息积压可能会导致系统性能下降,影响⽤户体验,甚⾄导致系统崩溃.因此,及时发现消息积压并解决对于维护系统稳定性⾄关重要.

3.2 解决方案

        遇到消息积压时,⾸先要分析消息积压造成的原因.根据原因来调整策略.主要从以下⼏个⽅⾯来解决:

1. 提高消费者效率

        a. 增加消费者实例数量,⽐如新增机器

        b. 优化业务逻辑,⽐如使⽤多线程来处理业务

        c. 设置 prefetchCount,当⼀个消费者阻塞时,消息转发到其他未阻塞的消费者.

        d. 消息发⽣异常时,设置合适的重试策略,或者转⼊到死信队列

2. 限制生产者速率.比如流量控制,限流算法等.

        a. 流量控制:在消息⽣产者中实现流量控制逻辑,根据消费者处理能⼒动态调整发送速率

        b. 限流:使⽤限流⼯具,为消息发送速率设置⼀个上限

        c. 设置过期时间.如果消息过期未消费,可以配置死信队列,以避免消息丢失,并减少对主队列的压⼒

3. 资源与配置优化.⽐如升级 RabbitMQ 服务器的硬件,调整 RabbitMQ 的配置参数等

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

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

相关文章

AIGC视频生成明星——Emu Video模型

大家好,这里是好评笔记,公主号:Goodnote,专栏文章私信限时Free。本文详细介绍Meta的视频生成模型Emu Video,作为Meta发布的第二款视频生成模型,在视频生成领域发挥关键作用。 🌺优质专栏回顾&am…

Debian 上安装PHP

1、安装软件源拓展工具 apt -y install software-properties-common apt-transport-https lsb-release ca-certificates 2、添加 Ondřej Sur 的 PHP PPA 源,需要按一次回车: add-apt-repository ppa:ondrej/php 3、更新软件源缓存: apt-g…

office 2019 关闭word窗口后卡死未响应

最近关闭word文件总是出现卡死未响应的状态,必须从任务管理器才能杀掉word 进程,然后重新打开word再保存,很是麻烦。(#其他特征,在word中打字会特别变慢,敲击键盘半秒才出现字符。) office官网…

SecureUtil.aes数据加密工具类

数据加密、解密工具类 包含map和vo的数据转换 import cn.hutool.core.bean.BeanUtil; import cn.hutool.crypto.SecureUtil;import java.util.HashMap; import java.util.Map;/*** 数据解析**/ public class ParamUtils {/*** 数据解密** param params 参数* param secretKe…

机器学习:支持向量机

支持向量机(Support Vector Machine)是一种二类分类模型,其基本模型定义为特征空间上的间隔最大的广义线性分类器,其学习策略便是间隔最大化,最终可转化为一个凸二次规划问题的求解。 假设两类数据可以被 H x : w T x…

SQL-leetcode—1148. 文章浏览 I

1148. 文章浏览 I Views 表: ---------------------- | Column Name | Type | ---------------------- | article_id | int | | author_id | int | | viewer_id | int | | view_date | date | ---------------------- 此表可能会存在重复行。(换句话说…

k8s资源预留

k8s资源预留 https://kubernetes.io/zh-cn/docs/tasks/administer-cluster/reserve-compute-resources/ vim /var/lib/kubelet/config.yamlenforceNodeAllocatable: - pods kubeReserved: # 配置 kube 资源预留cpu: 500mmemory: 1Giephemeral-storage: 1Gi systemReserved: #…

[STM32 HAL库]串口空闲中断+DMA接收不定长数据

一、空闲中断 STM32的串口具有空闲中断,什么叫做空闲呢?如何触发空闲中断呢? 空闲:串口发送的两个字符之间间隔非常短,所以在两个字符之间不叫空闲。空闲的定义是总线上在一个字节的时间内没有再接收到数据。触发条件…

Unity Line Renderer Component入门

Overview Line Renderer 组件是 Unity 中用于绘制连续线段的工具。它通过在三维空间中的两个或两个以上的点的数组,并在每个点之间绘制一条直线。可以绘制从简单的直线到复杂的螺旋线等各种图形。 1. 连续性和独立线条 连续性:Line Renderer 绘制的线条…

纯 Python、Django、FastAPI、Flask、Pyramid、Jupyter、dbt 解析和差异分析

一、纯 Python 1.1 基础概念 Python 是一种高级、通用、解释型的编程语言,以其简洁易读的语法和丰富的标准库而闻名。“纯 Python” 在这里指的是不依赖特定的 Web 框架或数据分析工具,仅使用 Python 原生的功能和标准库来开发应用程序或执行任务。 1.…

SQL记录学习日志

删除表 DROP TABLE:彻底删除表和其数据,无法恢复。 DROP TABLE IF EXISTS:在删除之前检查表是否存在。 TRUNCATE TABLE:删除所有数据,但保留表的结构。 DELETE:删除表中的所有数据,但保留表的结…

QT:tftp client 和 Server

1.TFTP简介 TFTP(Trivial File Transfer Protocol,简单文件传输协议)是TCP/IP协议族中的一个用来在客户机与服务器之间进行简单文件传输的协议,提供不复杂、开销不大的文件传输服务。端口号为69。 FTP是一个传输文件的简单协议,…

WPF5-x名称空间

1. x名称空间2. x名称空间内容3. x名称空间内容分类 3.1. x:Name3.2. x:Key3.3. x:Class3.4. x:TypeArguments 4. 总结 1. x名称空间 “x名称空间”的x是映射XAML名称空间时给它取的名字(取XAML的首字母),里面的成员(如x:Class、…

前端jquery 实现文本框输入出现自动补全提示功能

git仓库:web_study/some-demos/inputAutoFit at main Cong0925/web_study (github.com) 压缩包:已绑定到指定资源 示例图: 实现说明: 1.首先,html部分设置好相关的定位标签如图: 2.主要函数 3.默认数据

缓存之美:万文详解 Caffeine 实现原理(上)

由于社区最大字数限制,本文章将分为两篇,第二篇文章为缓存之美:万文详解 Caffeine 实现原理(下) 大家好,我是 方圆。文章将采用“总-分-总”的结构对配置固定大小元素驱逐策略的 Caffeine 缓存进行介绍&…

Qt实践:一个简单的丝滑侧滑栏实现

Qt实践:一个简单的丝滑侧滑栏实现 笔者前段时间突然看到了侧滑栏,觉得这个抽屉式的侧滑栏非常的有趣,打算这里首先尝试实现一个简单的丝滑侧滑栏。 首先是上效果图 (C,GIF帧率砍到毛都不剩了) QProperty…

工作流引擎Camunda与LiteFlow核心组件对比

以下为 Camunda 7 和 LiteFlow 详细的介绍,包括它们的核心组件和用途。 1. Camunda 7 详细介绍 Camunda 7 是一个基于 BPMN 2.0 标准的企业级工作流和决策自动化平台。它被广泛应用于复杂业务流程的管理和执行,其核心目标是通过流程自动化来提升企业效…

css动画水球图

由于echarts水球图动画会导致ios卡顿&#xff0c;所以纯css模拟 展示效果 组件 <template><div class"water-box"><div class"water"><div class"progress" :style"{ --newProgress: newProgress % }"><…

iOS 权限管理:同时请求相机和麦克风权限的最佳实践

引言 在开发视频类应用时&#xff0c;我们常常会遇到需要同时请求相机和麦克风权限的场景。比如&#xff0c;在用户发布视频动态时&#xff0c;相机用于捕捉画面&#xff0c;麦克风用于录制声音&#xff1b;又或者在直播功能中&#xff0c;只有获得这两项权限&#xff0c;用户…

Java 泛型上下限详解:以 Info 泛型类和方法实现为例

本文将通过一个实际示例&#xff0c;来深入讲解 Java 泛型中的上下限及其应用场景。在这个示例中&#xff0c;我们会实现一个泛型类 Info 和两个泛型方法 upperLimit 和 lowerLimit&#xff0c;并解释其工作机制。 1. 什么是 Java 泛型上下限&#xff1f; Java 泛型的上下限是…