【RabbitMQ】应用问题、仲裁队列(Raft算法)和HAProxy负载均衡
🔥个人主页: 中草药
🔥专栏:【中间件】企业级中间件剖析
一、幂等性保障
什么是幂等性?
幂等性是指对一个系统进行重复调用(相同参数),无论同一操作执行多少次,这些请求对系统的影响都是相同的效果,结果都与执行一次相同。
消息可能因网络重传、消费者异常重启、消息重复投递等导致重复消费,需确保多次处理不会产生副作用。
RabbitMQ 重复消息的来源
场景 | 原因 |
---|---|
生产者重复发送 | 生产者未收到 Broker 的 ACK,触发重试机制(如网络抖动、Broker 未及时响应) |
消费者重复消费 | 消费者处理消息后未及时 ACK,消息重新入队(如消费者崩溃、处理超时) |
Broker 消息堆积 | 消息因队列配置(如死信队列、TTL)被多次重新投递 |
MQ的幂等性保障
对于 MQ 而言,幂等性是指同一条消息,多次消费,对系统的影响是相同的。
一般消息中间件的消息传输保障分为三个层级。
- At most once: 最多一次。消息可能会丢失,但绝不会重复传输.
- At least once: 最少一次。消息绝不会丢失,但可能会重复传输.
- Exactly once: 恰好一次。每条消息肯定会被传输一次且仅传输一次.
RabbitMQ 支持 "最多一次" 和 "最少一次"。对于 "恰好一次", 目前 RabbitMQ 还做不到,不仅是 RabbitMQ, 目前市面上主流的消息中间件,都做不到这一点.
实现方案
1、唯一标识 + 去重表
原理:为每条消息分配唯一 ID(如 UUID、业务主键),消费前检查该 ID 是否已处理。
实现步骤:
生产者:在消息头(Header)中添加唯一标识(如 message_id
)。
消费者:
消费前查询去重表(如 Redis 或数据库),判断 message_id
是否存在。
若不存在,处理消息并写入去重表;若存在,直接 ACK 消息。
优化:
去重表设计:可以使用 Redis 的原子性操作 setnx 来保证幂等性,将唯一 ID 作为 key 放到 redis 中(SETNX messageID 1). 返回 1,说明之前没有消费过,正常消费。返回 0,说明这条消息之前已消费过,抛弃.
过期时间:为去重表记录设置 TTL,避免数据无限膨胀。
2、业务逻辑判断
在业务逻辑层面实现消息处理的幂等性。
例如: 通过检查数据库中是否已存在相关数据记录,或者使用乐观锁机制来避免更新已被其他事务更改的数据,再或者在处理消息之前,先检查相关业务的状态,确保消息对应的操作尚未执行,然后才进行处理,具体根据业务场景来处理
二、顺序性保障
在分布式系统中,消息的顺序性保障是确保消息按照生产者发送的先后顺序被消费者处理的机制。RabbitMQ 作为消息中间件,默认不提供严格的全局顺序保证,但可通过特定设计和配置实现部分场景下的顺序性。
顺序性问题的根源
RabbitMQ 默认无法保证全局顺序性的原因:
-
多消费者并行消费:一个队列绑定多个消费者时,消息可能被无序处理。
-
消息重试与重新入队:消费者处理失败的消息重新入队后,可能插入到队列中间。
-
交换机路由策略:使用
direct
、topic
或headers
交换机时,消息可能分散到不同队列。 -
网络延迟与分区:网络抖动可能导致消息到达 Broker 的顺序与发送顺序不一致。
顺序性保障方案
1、单一队列 + 单一消费者
-
原理:同一队列仅绑定一个消费者,串行处理消息。
-
适用场景:低吞吐量但对顺序性要求极高的场景(如金融交易)。
-
实现:
-
生产者将所有消息发送到同一队列。
-
队列仅允许一个消费者连接(设置
prefetch_count=1
)。 -
消费者禁用自动 ACK,处理完一条消息后手动确认。
-
2、分区消费
单个消费者的吞吐太低了,当需要多个消费者以提高处理速度时,可以使用分区消费,把一个队列分割成多个分区,每个分区由一个消费者处理,以此来保持每个分区内消息的顺序性.
Rabbitmq本身并不支持分区消费,需要业务逻辑去实现,或者借助spring-cloud-stream来实现
Partitioning with the RabbitMQ Binder :: Spring Cloud Stream
实现效果演示
3、消息确认机制
使用手动消息确认机制,消费者在处理完一条消息后,显式地发送确认,这样RabbitMQ才会移除并继续发送下一条消息.
4、业务逻辑控制
在某些情况下,即使消息乱序到达,也可以在业务逻辑层面实现顺序控制,比如通过在消息中嵌入序列号,并在消费时根据这些信息来处理
由于RabbitMO本身并不保证全局的严格顺序性,所以以上所提供的方案往往需要搭配混合使用,特别是在分布式系统中,在实际应用开发中,根据具体的业务需求,需要结合多种策略来实现所需要的顺序保证.
三、消息积压
常见原因
1、消息生产过快:在高流量或者高负载的情况下,生产者以极高的速率发送消息,超过了消费者的处理能力,包括一些流量激增的情况(活动促销)
2、消费者处理能力不足:消费者处理处理消息的速度跟不上消息生产的速度,也会导致消息在队列中积压,可能原因有:
- 消费端业务逻辑复杂,耗时长
- 消费端代码性能低
- 系统资源限制,如 CPU、内存、磁盘 I/O 等也会限制消费者处理消息的效率.
- 异常处理处理不当。消费者在处理消息时出现异常,导致消息无法被正确处理和确认.
3、网络问题:因为网络延迟或不稳定,消费者无法及时接收或确认消息,最终导致消息积压
4、RabbitMQ 服务器配置问题
- 未设置合理的
prefetch count
:消费者一次拉取过多消息,导致内存压力。 - 队列未持久化:重启后消息丢失,需重新处理积压。
- 未使用惰性队列(Lazy Queue):高吞吐场景下内存不足。
解决方案
1)提高消费者效率
a. 增加消费者实例数量,比如新增机器
b. 优化业务逻辑,比如使用多线程来处理业务
c. 设置 prefetchCount, 当一个消费者阻塞时,消息转发到其他未阻塞的消费者.
d. 消息发生异常时,设置合适的重试策略,或者转入到死信队列
2)限制生产者速率。比如流量控制,限流算法等
a. 流量控制:在消息生产者中实现流量控制逻辑,根据消费者处理能力动态调整发送速率
b. 限流:使用限流工具,为消息发送速率设置一个上限
c. 设置过期时间。如果消息过期未消费,可以配置死信队列,以避免消息丢失,并减少对主队列的压力
3)资源与配置优化 比如升级 RabbitMQ 服务器的硬件,调整 RabbitMQ 的配置参数等
在选择策略的时候需要实际考虑业务的需求和系统的实际承载能力
四、Raft算法
Raft 是一种专为 分布式一致性 设计的共识算法。其核心目标是通过 强可理解性 解决传统 Paxos 算法的复杂性,同时保证分布式系统的 高可用性 和 数据一致性。
分解问题
将共识问题拆分为三个子问题:
领导人选举(Leader Election):系统中仅有一个 Leader 负责处理客户端请求。
日志复制(Log Replication):Leader 将操作日志同步到所有 Follower 节点。
安全性(Safety):确保所有节点最终状态一致,避免数据冲突。
核心机制
节点角色
-
Leader:唯一处理客户端请求的节点,负责日志复制和心跳维持。
-
Follower:被动接收 Leader 的日志和心跳,不主动响应客户端,不直接处理客户端请求。
-
Candidate:选举过程中的临时角色(Follower 超时未收到心跳后成为 Candidate,开始尝试通过 投票过程成为新的Leader)。
正常的情况下,集群中只有一个Leader,剩下的节点都是follower
任期(Term)
-
全局单调递增的整数(类似“逻辑时钟”),每个任期至多一个 Leader。
-
节点间通信携带 Term,用于检测过期信息(如旧 Leader 的请求会被拒绝)。
Raft 将时间划分成任意长度的任期(term).每一段任期从一次选举开始,在这个时候会有一个或者多个candidate 尝试去成为leader,在成功完成一次leaderelection之后,一个leader就会一直节管理集群直到任期结束,在某些情况下,一次选举无法选出 leader,这个时候这个任期会以没有leader 而结束(如下图t3).同时一个新的任期(包含一次新的选举)会很快重新开始
通信
Raft算法中的服务器节点之间采用RPC进行通信,主要由两类RPC请求:
-
RequestVote RPCs: 请求投票,由 candidate 在选举过程中发出
-
AppendEntries RPCs: 追加条目,由leader 发出,用来做日志复制和提供心跳机制
选举过程
可以通过此网站动画来理解投票选举过程Raft Consensus Algorithm
Raft 采用一种心跳机制来触发 leader 选举,当服务器启动的时候,都是follow状态.如果follower在election timeout内没有收到来自leader的心跳(可能没有选出leader,也可能leader挂了,或者leader与follower之间网络故障),则会主动发起选举.
步骤如下:
1、率先超时的节点,自增当前任期号然后切换为 candidate 状态,并投自己一票
2、以并行的方式发送一个 RequestVote RPCs 给集群中的其他服务器节点(企图得到它们的投票)
3、等待其他节点的回复
此时可能会出现三种结果
a、赢得选举,自己成为Leader(包括自己的一票),新的Leader会给其他节点发布消息,避免其余节点触发新的选举
b、其他节点赢得了选举,未成功选举的节点在接受到消息时,会自动转化为follower
c、一段时间内没有收到majority投票,保持candidate状态,重新发出选举
没有任何节点获得majority投票.比如所有的 follower 同时变成 candidate,然后它们都将票投给自己,那这样就没有 candidate 能得到超过半数的投票了.当这种情况发生的时候,每个candidate 都会进行一次超时响应,然后通过自增任期号来开启一轮新的选举,并启动另一轮的RequestVote RPCs.如果没有额外的措施,这种无结果的投票可能会无限重复下去.
为了解决上述问题,Raft 采用 随机选举超时时间(randomized election timeouts)来确保很少产生无结果的投票,并且就算发生了也能很快地解决。为了防止选票一开始就被瓜分,选举超时时间是从一个固定的区间(比如,150-300ms)中随机选择。这样可以把服务器分散开来以确保在大多数情况下会只有一个服务器率先结束超时,那么这个时候,它就可以赢得选举并在其他服务器结束超时之前发送心跳。
五、仲裁队列
RabbitMQ 的 仲裁队列(Quorum Queues) 是 RabbitMQ 3.8 版本引入的一种新型队列类型,专为 高可用性和数据一致性 场景设计。它基于 Raft 一致性协议实现,替代了传统的镜像队列(Mirrored Queues),在节点故障时能更可靠地保证数据安全。
在集群环境之中,如果某一节点宕机故障,其中原本的信息也会发生丢失,仲裁队列可以在rabbitmq之间进行队列数据的复制,保障集群系统的高可用性。
节点宕机之前
节点宕机后,消息丢失了
使用仲裁队列
@Bean("quorumQueue")
public Queue quorumQueue() {return QueueBuilder.durable("quorum_queue").quorum().build();
}
可以观察到,仲裁队列后面有一个+2,表示队列中有两个镜像节点,点进去可以看到队列详细
此时如果发生单个节点宕机,队列里的消息不会丢失
六、HAProxy负载均衡
面对大量的业务访问,高并发请求,试想如果一个集群中有3个节点,我们在写代码时,访问哪个节点呢?
答案是访问任何一个节点都可以.
这时候就存在两个问题:
1、如果我们访问的是node1,但是node1挂了,咱们的程序也会出现问题,所以最好是有一个统一的入口,一个节点故障时,流量可以及时转移到其他节点.
2、如果所有的客户端都与node1建议连接,那么node1的网络负载必然会大大增加,而其他节点又由于没有那么多的负载而造成硬件资源的浪费.
这时,负载均衡显得尤为重要,HAProxy(High Availability Proxy)是一款开源的 高性能TCP/HTTP负载均衡器 和 反向代理,广泛用于分发流量、提升系统可用性和扩展性。
快速上手
Ubuntu安装
#更新软件包
sudo apt-get update#查找haproxy
sudo apt listlgrep haproxy#安装haproxy
sudo apt-get install haproxy
验证安装
#查看服务状态
sudo systemctl status haproxy#查看版本
haproxy -v#如果要设置HAProxy服务开机自启,可以使用
sudo systemctl enable haproxy
修改haproxy.cfg
vim /etc/haproxy/haproxy.cfg
# haproxy web 管理界面
listen stats #设置一个监听器,统计HAProxy的统计信息bind *:8100 #指定了监听器绑定到的IP地址和端口mode http #监听器的工作模式为HTTPstats enable #启用统计页面stats realm Haproxy\ Statisticsstats uri /stats auth admin:admin #登录账号密码
# 配置负载均衡
Listen rabbitmgbind *:5670mode tcp #Rabbitmq使用的AMQP协议是一个基于TCP的协议balance roundrobin #制定负载均衡策略为轮询server rabbitmgl 127.0.0.1:5672 check inter 5000 rise 2 fall 3server rabbitmq2 127.0.0.1:5673 check inter 5000 rise 2 fall 3server rabbitmg3 127.0.0.1:5674 check inter 5000 rise 2 fall 3
重启HAProxy
sudo systemctl restart haproxy
此时可以通过访问 http://ip:8100/ 查看HAProxy
修改配置文件
spring:rabbitmq:addresses: amqp://study:study@ip:5670/Test
此时成功实现了负载均衡,也实现了节点宕机后,流量的及时转移
自信与骄傲有异:信者常沉着,而骄傲者常浮扬。 ——梁启超
🍀🍀🍀🍀🍀🍀🍀🍀🍀🍀🍀🍀🍀🍀🍀🍀🍀🍀🍀🍀🍀🍀🍀🍀🍀🍀🍀🍀
以上,就是本期的全部内容啦,若有错误疏忽希望各位大佬及时指出💐
制作不易,希望能对各位提供微小的帮助,可否留下你免费的赞呢🌸
相关文章:

【RabbitMQ】应用问题、仲裁队列(Raft算法)和HAProxy负载均衡
🔥个人主页: 中草药 🔥专栏:【中间件】企业级中间件剖析 一、幂等性保障 什么是幂等性? 幂等性是指对一个系统进行重复调用(相同参数),无论同一操作执行多少次,这些请求…...

软件设计师-错题笔记-系统开发与运行
1. 解析: A:模块是结构图的基本成分之一,用矩形表示 B:调用表示模块之间的调用关系,通过箭头等符号在结构图中体现 C:数据用于表示模块之间的传递的信息,在结构图中会涉及数据的流向等表示 …...
硬件设备基础
一、ARM9 内核中有多少个通用寄存器?其中 sp、lr、pc、cpsr、spsr 的作用是什么? 在 ARM9 内核中,寄存器组织包含 37 个 通用寄存器,其中,有 13 个通用目的寄存器(R0 - R12)。 S3C2440 是 ARM 架…...
[编程基础] PHP · 学习手册
🔥 《PHP 工程师修炼之路:从零构建系统化知识体系》 🔥 🛠️ 专栏简介: 这是一个以工业级开发标准打造的 PHP 全栈技术专栏,涵盖语法精粹、异步编程、Zend引擎原理、框架源码、高并发架构等全维度知识体系…...

C#简易Modbus从站仿真器
C#使用NModbus库,编写从站仿真器,支持Modbus TCP访问,支持多个从站地址和动态启用/停用从站(模拟离线),支持数据变化,可以很方便实现,最终效果如图所示。 项目采用.net framework 4.…...
Error parsing column 10 (YingShou=-99.5 - Double) dapper sqlite
在使用sqlite 调取 dapper的时候出现这个问题提示: 原因是 在 sqlite表中设定的字段类型是 decimel而在C#的字段属性也是decimel,结果解析F负数 小数的时候出现这个错误提示: 解决办法:使用默认的sqlite的字段类型来填入 REAL描述…...
Spring AI系列——使用大模型对文本进行内容总结归纳分析
一、技术原理与架构设计 1. 技术原理 本项目基于 Spring AI Alibaba 框架,结合 DashScope 大模型服务 实现文本内容的自动摘要和结构化输出。核心原理如下: 文档解析: 使用 TikaDocumentReader 解析上传的文件(如 PDF、Word 等&…...
【深度学习】目标检测算法大全
目录 一、R-CNN 1、R-CNN概述 2、R-CNN 模型总体流程 3、核心模块详解 (1)候选框生成(Selective Search) (2)深度特征提取与微调 2.1 特征提取 2.2 网络微调(Fine-tuning) …...
5.1.1 WPF中Command使用介绍
WPF 的命令系统是一种强大的输入处理机制,它比传统的事件处理更加灵活和可重用,特别适合 MVVM (Model, View, ViewModel)模式开发。 一、命令系统核心概念 1.命令系统基本元素: 命令(Command): 即ICommand类,使用最多的是RoutedCommand,也可以自己继承ICommand使用自定…...
excel大表导入数据库
前文介绍了数据量较小的excel表导入数据库的方法,在数据量较大的情况下就不太适合了,一个是因为mysql命令的执行串长度有限制,二是node-xlsx这个模块加载excel文件是整个文件全部加载到内存,在excel文件较大和可用内存受限的场景就…...
《让歌声跨越山海:Flutter借助Agora SDK实现高质量连麦合唱》
对于Flutter开发者而言,借助Agora SDK实现这一功能,不仅能为用户带来前所未有的社交体验,更是在激烈的市场竞争中脱颖而出的关键。 Agora SDK作为实时通信领域的佼佼者,拥有一系列令人瞩目的特性,使其成为实现高质量连…...
A* (AStar) 寻路
//调用工具类获取路线 let route AStarSearch.getRoute(start_point, end_point, this.mapFloor.map_point); map_point 是所有可走点的集合 import { _decorator, Component, Node, Prefab, instantiate, v3, Vec2 } from cc; import { oops } from "../../../../../e…...
单词短语0512
当然可以,下面是“opportunity”在考研英语中的常用意思和高频短语,采用大字体展示,便于记忆: ✅ opportunity 的考研常用意思: 👉 机会,良机 表示有利的时机或条件,尤指成功的可能…...

视觉-语言-动作模型:概念、进展、应用与挑战(下)
25年5月来自 Cornell 大学、香港科大和希腊 U Peloponnese 的论文“Vision-Language-Action Models: Concepts, Progress, Applications and Challenges”。 视觉-语言-动作 (VLA) 模型标志着人工智能的变革性进步,旨在将感知、自然语言理解和具体动作统一在一个计…...

一键解锁嵌入式UI开发——LVGL的“万能配方”
面对碎片化的嵌入式硬件生态,LVGL堪称开发者手中的万能配方。它通过统一API接口屏蔽底层差异,配合丰富的预置控件(如按钮、图表、滑动条)与动态渲染引擎,让工程师无需深入图形学原理,效率提升肉眼可见。 L…...
C# NX二次开发:宏录制实战讲解(第一讲)
今天要讲的是关于NX软件录制宏操作的一些案例。 下面讲如何在NX软件中复制Part体的录制宏。 NXOpen.Session theSession NXOpen.Session.GetSession(); NXOpen.Part workPart theSession.Parts.Work; NXOpen.Part displayPart theSession.Parts.Display; NXOpe…...
记录裁员后的半年前端求职经历
普通的人生终起波澜 去年下半年应该算是我毕业以来发生人生变故最多的一段时间。 先是 7 月份的时候发作了一次急性痛风,一个人在厦门,坐在床上路都走不了,那时候真的好想旁边能有个人能扶我去医院,真的是感受到 10 级的孤独。尝…...
Linux 文件查看|查找|压缩|解压 常用命令
cat 连接文件并打印到标准输出设备上 指令备注cat aaa.txt连接文件aaa并打印到标准输出设备上 more 以全屏幕的方式按页显示文本文件的内容 按Space键:显示文本的下一屏内容 按Enier键:只显示文本的下一行内容 指令备注more aaa.txt查看文件aaa le…...
什么是:Word2Vec + 余弦相似度
什么是:Word2Vec + 余弦相似度 目录 什么是:Word2Vec + 余弦相似度示例文本基于Word2Vec的文本向量化计算余弦相似度Word2Vec不是基于Transformer架构的Word2Vec是一种将单词转化为向量表示的模型,而Word2Vec + 余弦相似度则是一种利用Word2Vec得到的向量来计算文本相似性的…...

智慧城市综合运营管理系统Axure原型
这款Axure原型的设计理念紧紧围绕城市管理者的需求展开。它旨在打破传统城市管理中信息孤岛的局面,通过统一标准接入各类业务系统,实现城市运营管理信息资源的全面整合与共享。以城市管理者为中心,为其提供一个直观、便捷、高效的协同服务平台…...
[学习]RTKLib详解:convkml.c、convrnx.c与geoid.c
RTKLib详解: datum.c、download.c 与 lambda.c 本文是 RTKLlib详解 系列文章的一篇,目前该系列文章还在持续总结写作中,以发表的如下,有兴趣的可以翻阅。 [学习] RTKlib详解:功能、工具与源码结构解析 [学习]RTKLib详解ÿ…...

Qwen智能体qwen_agent与Assistant功能初探
Qwen智能体qwen_agent与Assistant功能初探 一、Qwen智能体框架概述 Qwen(通义千问)智能体框架是阿里云推出的新一代AI智能体开发平台,其核心模块qwen_agent.agent提供了一套完整的智能体构建解决方案。该框架通过模块化设计,将L…...
LayerNorm vs RMSNorm 技术对比
1. 核心概念 LayerNorm (层归一化) 思想:对单个样本的所有特征维度进行归一化目标:使每个样本的特征分布 μ 0 \mu0 μ0, σ 1 \sigma1 σ1特点:同时调整均值和方差 RMSNorm (均方根归一化) 思想:基于均方根的简…...

可视化图解算法37:序列化二叉树-II
1. 题目 描述 请实现两个函数,分别用来序列化和反序列化二叉树,不对序列化之后的字符串进行约束,但要求能够根据序列化之后的字符串重新构造出一棵与原二叉树相同的树。 二叉树的序列化(Serialize)是指:把一棵二叉树按照某种遍…...

C++GO语言微服务和服务发现②
01 创建go-micro项目-查看生成的 proto文件 02 创建go-micro项目-查看生成的main文件和handler ## 创建 micro 服务 命令:micro new --type srv test66 框架默认自带服务发现:mdns。 使用consul服务发现: 1. 初始consul服务发现&…...

【Web前端开发】CSS基础
2.CSS 2.1CSS概念 CSS是一组样式设置的规则,称为层叠样式表,用于控制页面的外观样式。 使用CSS能够对网页中元素位置的排版进行像素控制,实现美化页面的效果,也能够做到页面的样式和结构分离。 2.2基本语法 通常都是ÿ…...
Google LLM prompt engineering(谷歌提示词工程指南)
文章目录 基本概念AI输出配置:调整AI的回答方式输出长度温度(Temperature)Top-K和Top-P 提示技术:让AI更好地理解你零样本提示(Zero-shot)少样本提示(Few-shot)系统提示(…...
接口出现 请求参数格式错误 的解决方法
目录 前言1. 问题所示2. 原理分析3. 解决方法前言 🤟 找工作,来万码优才:👉 #小程序://万码优才/r6rqmzDaXpYkJZF 爬虫神器,无代码爬取,就来:bright.cn Java基本知识: java框架 零基础从入门到精通的学习路线 附开源项目面经等(超全)【Java项目】实战CRUD的功能整理…...

Git实战经验分享:深入掌握git commit --amend的进阶技巧
一、工具简介 git commit --amend是Git版本控制系统的核心补救命令,主要用于修正最近一次提交的元数据。该命令不会产生新的提交记录,而是通过覆盖原提交实现版本历史的整洁性,特别适合在本地仓库进行提交优化。 二、核心应用场景 提交信息…...

PTA:jmu-ds-最短路径
给定一个有向图,规定源点为0,求源点0到其他顶点最短路径。###你要实现的 函数接口定义: void Dijkstra(MGraph g,int v);//源点v到其他顶点最短路径 裁判测试程序样例: #include <stdio.h> #include <iostream> …...