【RabbitMQ】幂等性、顺序性
幂等性
概述
幂等性是数学和计算机科学中某些运算的性质,他们可以被多次应用,而不会改变初始应用的结果。RabbitMQ的幂等性则是指同一条消息,多次消费,对系统的影响是相同的。
一般消息中间件的消息传输保障分为三个层级:
- 最少一次(At least once):消息绝对不会丢失,但可能会重复传输。
- 恰好一次(Exactly once):每条消息肯定会被传输一次且仅传输一次。
- 最多一次(At most once):消息可能会丢失,但绝对不会重复传输。
RabbitMQ支持最少一次和最多一次。对于恰好一次,RabbitMQ目前做不到,而且很多消息中间件都做不到这一点。
在业务使用中,对于可靠性要求较高的场景,建议使用最少一次,以防消息丢失。最多一次会因为消息发送过程中,网络问题、消费出现异常等种种原因,导致消息丢失。但是最少一次,就会使得消费端可能收到重复的消息,也会造成对同一条消息的多次处理。对于一些比较重要的业务而言,重复处理相同的消息,就会造成严重事故。例如:当用户对一个订单付款之后,因为网络问题,付款成功的结果未返回给订单系统,当用户再次点击付款时,如果系统未做幂等性处理,那就会造成两次扣款。
以下场景可能会导致消息重复发送:
发送时消息重复:当一条消息已被成功发送到Broker并完成持久化,此时出现了网络闪断或者客户端宕机,导致Broker对客户端应答失败,如果此时生产者意识到发送失败并尝试再次发送消息,消费者后续会收到两条内容相同并且MessageID也相同的消息。
投递时消息重复:消息消费的场景下,消息已投递到消费者并完成业务处理,当客户端给服务端反馈应答的时候网络中断。为了保证消息至少被消费一次,消息队列会在网络恢复之后再次尝试投递之前已经被处理过的消息,消费者后续会收到两条内容相同并且MessageID也相同的消息。
解决方案
全局唯一ID
首先,为每条消息分配一个唯一标识符,例如雪花算法、UUID等,只要能保证唯一性即可。
其次,消费者收到消息后,先用唯一标识符判断该消息是否已经消费过,如果消费过就直接放弃。
最后,如果没有消费过,消费者就开始消费信息,业务处理成功之后,把唯一标识符保存起来。
可以使用Redis的原子性操作setnx来保证幂等性,将唯一标识符作为KEY放到Redis中。消费消息之前,先SETNX ID 1。如果返回值为1,表示之前没有消费过,正常消费。返回值为0,则表示这条消息之前已消费过,直接抛弃。
业务逻辑判断
在业务逻辑层面实现消息处理的幂等性。
可以使用数据库来保证幂等性,通过检查数据库是否已经存在相关数据记录;也可以使用锁机制来保证幂等性,通过使用乐观锁机制来避免更新已经被其他事务更改的数据;还可以使用相关业务状态来保证幂等性,在消费者处理消息之前,先检查相关的业务状态,确保消息对应的操作尚未执行,然后才进行处理,具体根据业务场景来做。
顺序性
概述
消息的顺序性是指消费者消费的消息和生产者发送消息的顺序是一致的。例如生产者发送的消息依次是msg1、msg2、msg3,那么消费者消费消息的顺序也必须按照msg1、msg2、msg3的顺序进行。
很多业务场景下,消息的消费是不用保证顺序的,比如使用MQ实现订单超时的处理。但是有些业务场景下,可能存在多个消息顺序处理的情况,比如用户信息修改,对同一个用户的同一个资料进行修改,需要保证消息的顺序。
在实际场景中,如果只有一个生产者,也只有一个消费者,并且不考虑消息丢失、网络故障等异常情况,是可以保证消息的顺序性。但是,如果有多个生产者同时发送消息,无法确定消息到达Broker的前后顺序,也就无法验证消息的顺序性。
打破顺序性的场景:
- 多个消费者:当队列配置了多个消费者时,消息可能会被不同的消费者并行处理,从而导致消息处理的顺序性无法保证。
- 网络波动或异常:在消息传递的过程中,如果出现网络波动或异常,可能会导致ACK(消息确认)丢失,从而使得消息被重新入队和重新消费,造成顺序性问题。
- 消息重试:如果消费者在处理消息后未能及时发送确认,或者确认消息在传输过程中丢失,那么MQ可能会认为消息未被成功消费而进行重试,这也可能导致消息处理的顺序性问题。
- 消息路由问题:在复杂的路由场景中,消息可能会根据路由键被发送到不同的队列,从而无法保证全局的顺序性。
- 死信队列:消息因为某些原因进入死信队列,死信队列被消费时,无法保证消息的顺序和生产者发送消息的顺序一致。
打破顺序性的场景包括但不限于以上几种,如果要保证消息的顺序性,那就需要对消息进行进一步的处理。
解决方案
消息顺序性保障分为:局部顺序性保障和全局顺序性保障。
局部顺序性保障指的是在单个队列内部保证消息的顺序。全局顺序性保障指的是在多个队列或多个消费者之间保障消息的顺序。
在实际工作中,全局顺序性难以实现,可以考虑使用业务逻辑来保障顺序性。比如在消息中嵌入序列号,并在消费端进行排序处理。相对而言,局部顺序性更常见,也更容易实现。
RabbitMQ作为一个分布式消息队列,主要优化的是吞吐量和可用性,而不是严格的顺序性保障。如果业务场景确实需要严格的消息顺序,可能需要在应用层面进行额外的设计和实现。
单队列单消费者
最简单的实现顺序性保障的方法一定是,使用单个队列,并由单个消费者进行处理。同一个队列中的消息是先进先出的,这就足以实现消息的顺序性。
分区消费
虽然单消费者单队列可以完美保障消息的顺序性,但是其吞吐太低了。当需要多个消费者以提高处理速度时,可以使用分区消费。把一个队列分割成多个分区,每个分区由一个消费者处理,以此保障每个分区内消息的顺序性。例如最开始的修改资料这一业务,我们只需要保障每个用户的消息顺序性即可,因此我们可以使用分区消费来做(可以借助Spring-CLoud-Stream来实现)。
消息确认机制
使用手动消息确认机制,消费者在处理完一条消息后,显式地发送确认,这样RabbitMQ才会移除并继续发送下一条消息。
业务逻辑控制
在某些情况下,即使消息乱序到达,也可以在业务逻辑层面实现顺序控制。比如通过在消息中嵌入序列号,并在消费时根据这些信息来处理。
RabbitMQ本身并不能保证全局的严格顺序性。在实际开发中,根据具体的业务需求,可能需要结合多种策略来实现所需要的顺序保证。
相关文章:
【RabbitMQ】幂等性、顺序性
幂等性 概述 幂等性是数学和计算机科学中某些运算的性质,他们可以被多次应用,而不会改变初始应用的结果。RabbitMQ的幂等性则是指同一条消息,多次消费,对系统的影响是相同的。 一般消息中间件的消息传输保障分为三个层级&#…...
FFmpeg源码:avio_skip函数分析
AVIOContext结构体和其相关的函数分析: FFmpeg源码:avio_r8、avio_rl16、avio_rl24、avio_rl32、avio_rl64函数分析 FFmpeg源码:read_packet_wrapper、fill_buffer函数分析 FFmpeg源码:avio_read函数分析 FFmpeg源码ÿ…...
Llama 3.1 技术研究报告-6
6 推理 我们研究了两种主要技术,以使 Llama 3 405B 模型的推理⾼效:(1) 流⽔线并⾏和 (2) FP8 量化。我们已经公开发布了我们的 FP8 量化实现。 6.1 流⽔线并⾏ 当使⽤ BF16 数字表⽰模型参数时,Llama 3 405B 不适合在装有 8 个 Nvidia H1…...
更新日志-Python OS
这么久没更新全是因为这段时间的事情很多,只能一点一点的更新代码,不过好在,也是成功更新出来啦! 更新日志(2024/9/29) 代码全文更新,将所有的绝对路径替换为相对路径,这样在各位大…...
Chrome浏览器的C++内存管理技术揭秘
Chrome浏览器作为全球最流行的网络浏览器之一,其高效的内存管理技术功不可没。本文将深入探讨Chrome浏览器在C中的内存管理技术,并介绍如何通过调整网页加载时间、优化视频播放体验和解决谷歌浏览器占用CPU过高的问题来提升浏览器性能。 (本…...
Redis --- redis事务和分布式事务锁
redis事务基本实现 Redis 可以通过 MULTI,EXEC,DISCARD 和 WATCH 等命令来实现事务(transaction)功能。 > MULTI OK > SET USER "Guide哥" QUEUED > GET USER QUEUED > EXEC 1) OK 2) "Guide哥"使用 MULTI命令后可以输入…...
SQL,将多对多的关联记录按行输出
数据库的Primary表和Secondary表有相同的结构,其中W、H、D是主键。Primary表:NameWHDPrimary item 1100500300Primary item 2100600300Primary item 3200500300Primary item 4100500300Primary item 5100600300Primary item 6200500300 Secondary表&…...
【SQL】筛选字符串与正则表达式
目录 语法 需求 示例 分析 代码 语法 SELECT column1, column2, ... FROM table_name WHERE condition; WHERE 子句用于指定过滤条件,以限制从数据库表中检索的数据。当你执行一个查询时,WHERE 子句允许你筛选出满足特定条件的记录。如果记录满…...
【Redis入门到精通五】Java如何像使用MySQL一样使用Redis(jedis安装及使用)
目录 Jedis 1.jedis是什么 2.jedis的安装配置 3.jedis的基础命令操作展示 1.set和get操作: 2.exists和del操作: 3.keys和type操作: 4. expire和ttl: Jedis Java 操作 redis 的客⼾端有很多,其中最知名的是 jedi…...
【 微信机器人+ AI 搭建】
摘要: 各种大模型已经出来好久了,各类app也已经玩腻了,接下来,就在考虑,怎么让大模型,利益最大化。 本人没有显著的家世,没有富婆包养,只能自己抽点时间,研究下技术&…...
VGG16网络介绍及代码撰写详解(总结1)
可以从本人以前的文章中可以看出作者以前从事的是嵌入式控制方面相关的工作,是一个机器视觉小白,之所以开始入门机器视觉的学习只要是一个idea,想把机器视觉与控制相融合未来做一点小东西。废话不多说开始正题。 摘要:本文是介绍V…...
多个excel表数据比对操作
多个excel表数据比对操作 本文主要使用两种方法进行比对,分别使用了openpyxl第三方库和pandas第三方库进行数据比对 两种方法优缺点: openpyxy: 优点:主要是处理xlsx的文件,里面方法简单,易懂 缺点:当数据量大的时候,速度很慢,之前我一条一条数据拿出来比较,两百多条…...
golang学习笔记32——哪些是用golang实现的热门框架和工具
推荐学习文档 golang应用级os框架,欢迎stargolang应用级os框架使用案例,欢迎star案例:基于golang开发的一款超有个性的旅游计划app经历golang实战大纲golang优秀开发常用开源库汇总想学习更多golang知识,这里有免费的golang学习笔…...
ZYNQ:开发环境搭建
资料下载 http://47.111.11.73/docs/boards/fpga/zdyz_qimxing(V2).html Vivado软件是什么? Vivado软件是Xilinx(赛灵思)公司推出的一款集成设计环境(IDE),主要用于FPGA(现场可编程门阵列&am…...
一步一步丰富生成式语言模型系统
以下是这套生成式语言模型解决任务的流程图概述: #mermaid-svg-sRHDSMUMV1utrg2F {font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}#mermaid-svg-sRHDSMUMV1utrg2F .error-icon{fill:#552222;}#mermaid-svg-sRHDSMUMV1u…...
Python中元组的常用方法
# 在Python中,元组(tuple)是一种不可变的序列类型,用于存储多个元素。元组的特点包括: # # 不可变性:一旦创建,元组的元素不能改变。这意味着不能添加、删除或修改元组中的元素。 # 可以包含任何…...
新版本Android Studio如何新建Java code工程
新版本Android Studio主推Kotlin,很多同学以为无法新建Java工程了,其实是可以的,如果要新建Java代码的Android工程,在New Project的时候需要选择Empty Views Activity,如图所示,gradle也建议选为build.grad…...
2024年世界职业院校技能大赛:全面升级的国际化职业技能竞赛
近日,中华人民共和国教育部发布了《2024年世界职业院校技能大赛实施方案》,宣布从2024年起将全国职业院校技能大赛升级为世界职业院校技能大赛。这一重大决策不仅标志着我国职业教育竞赛平台的全面国际化,更彰显了中国在全球职业教育领域的引领作用和战略眼光,具体内…...
前端vue相关常见面试题,包含MVVM、双向绑定原理、性能优化、vue2和vue3性能对比等
vue面试题 MVVM 概念 model view viewModel 本质上是mvc(程序分层开发思想) 将viewModel的状态和行为抽象化,viewmodel将视图ui和业务逻辑分开,去除model的数据,同时处理view中需要展示的内容和业务逻辑 view视图层 …...
生信初学者教程(十二):数据汇总
文章目录 介绍加载R包导入数据汇总表格输出结果总结介绍 在本教程中,汇总了三个肝细胞癌(HCC)的转录组数据集,分别是LIRI-JP,LIHC-US/TCGA-LIHC和GSE14520,以及一个HCC的单细胞数据集GSE149614的临床表型信息。这些数据集为科研人员提供了丰富的基因表达数据和相关的临床…...
19c补丁后oracle属主变化,导致不能识别磁盘组
补丁后服务器重启,数据库再次无法启动 ORA01017: invalid username/password; logon denied Oracle 19c 在打上 19.23 或以上补丁版本后,存在与用户组权限相关的问题。具体表现为,Oracle 实例的运行用户(oracle)和集…...
docker详细操作--未完待续
docker介绍 docker官网: Docker:加速容器应用程序开发 harbor官网:Harbor - Harbor 中文 使用docker加速器: Docker镜像极速下载服务 - 毫秒镜像 是什么 Docker 是一种开源的容器化平台,用于将应用程序及其依赖项(如库、运行时环…...
JVM垃圾回收机制全解析
Java虚拟机(JVM)中的垃圾收集器(Garbage Collector,简称GC)是用于自动管理内存的机制。它负责识别和清除不再被程序使用的对象,从而释放内存空间,避免内存泄漏和内存溢出等问题。垃圾收集器在Ja…...
华为OD机试-食堂供餐-二分法
import java.util.Arrays; import java.util.Scanner;public class DemoTest3 {public static void main(String[] args) {Scanner in new Scanner(System.in);// 注意 hasNext 和 hasNextLine 的区别while (in.hasNextLine()) { // 注意 while 处理多个 caseint a in.nextIn…...
Nginx server_name 配置说明
Nginx 是一个高性能的反向代理和负载均衡服务器,其核心配置之一是 server 块中的 server_name 指令。server_name 决定了 Nginx 如何根据客户端请求的 Host 头匹配对应的虚拟主机(Virtual Host)。 1. 简介 Nginx 使用 server_name 指令来确定…...
C# SqlSugar:依赖注入与仓储模式实践
C# SqlSugar:依赖注入与仓储模式实践 在 C# 的应用开发中,数据库操作是必不可少的环节。为了让数据访问层更加简洁、高效且易于维护,许多开发者会选择成熟的 ORM(对象关系映射)框架,SqlSugar 就是其中备受…...
AI编程--插件对比分析:CodeRider、GitHub Copilot及其他
AI编程插件对比分析:CodeRider、GitHub Copilot及其他 随着人工智能技术的快速发展,AI编程插件已成为提升开发者生产力的重要工具。CodeRider和GitHub Copilot作为市场上的领先者,分别以其独特的特性和生态系统吸引了大量开发者。本文将从功…...
CSS | transition 和 transform的用处和区别
省流总结: transform用于变换/变形,transition是动画控制器 transform 用来对元素进行变形,常见的操作如下,它是立即生效的样式变形属性。 旋转 rotate(角度deg)、平移 translateX(像素px)、缩放 scale(倍数)、倾斜 skewX(角度…...
嵌入式学习之系统编程(九)OSI模型、TCP/IP模型、UDP协议网络相关编程(6.3)
目录 一、网络编程--OSI模型 二、网络编程--TCP/IP模型 三、网络接口 四、UDP网络相关编程及主要函数 编辑编辑 UDP的特征 socke函数 bind函数 recvfrom函数(接收函数) sendto函数(发送函数) 五、网络编程之 UDP 用…...
rknn toolkit2搭建和推理
安装Miniconda Miniconda - Anaconda Miniconda 选择一个 新的 版本 ,不用和RKNN的python版本保持一致 使用 ./xxx.sh进行安装 下面配置一下载源 # 清华大学源(最常用) conda config --add channels https://mirrors.tuna.tsinghua.edu.cn…...
