【RabbitMQ】应用问题、仲裁队列(Raft算法)和HAProxy负载均衡

news2025/5/13 17:40:41

    🔥个人主页: 中草药

🔥专栏:【中间件】企业级中间件剖析


一、幂等性保障

什么是幂等性?

        幂等性是指对一个系统进行重复调用(相同参数),无论同一操作执行多少次,这些请求对系统的影响都是相同的效果,结果都与执行一次相同。

        消息可能因网络重传、消费者异常重启、消息重复投递等导致重复消费,需确保多次处理不会产生副作用。

RabbitMQ 重复消息的来源

场景原因
生产者重复发送生产者未收到 Broker 的 ACK,触发重试机制(如网络抖动、Broker 未及时响应)
消费者重复消费消费者处理消息后未及时 ACK,消息重新入队(如消费者崩溃、处理超时)
Broker 消息堆积消息因队列配置(如死信队列、TTL)被多次重新投递

MQ的幂等性保障

对于 MQ 而言,幂等性是指同一条消息,多次消费,对系统的影响是相同的。

一般消息中间件的消息传输保障分为三个层级。

  1. At most once: 最多一次。消息可能会丢失,但绝不会重复传输.
  2. At least once: 最少一次。消息绝不会丢失,但可能会重复传输.
  3. 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 默认无法保证全局顺序性的原因:

  • 多消费者并行消费:一个队列绑定多个消费者时,消息可能被无序处理。

  • 消息重试与重新入队:消费者处理失败的消息重新入队后,可能插入到队列中间。

  • 交换机路由策略:使用 directtopic 或 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          #监听器的工作模式为HTTP
    stats enable       #启用统计页面
    stats realm Haproxy\ Statistics
    stats uri /
    stats auth admin:admin    #登录账号密码
# 配置负载均衡
Listen rabbitmg
    bind *:5670
    mode tcp              #Rabbitmq使用的AMQP协议是一个基于TCP的协议
    balance roundrobin    #制定负载均衡策略为轮询
    server    rabbitmgl 127.0.0.1:5672 check inter 5000 rise 2 fall 3
    server    rabbitmq2 127.0.0.1:5673 check inter 5000 rise 2 fall 3
    server    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

此时成功实现了负载均衡,也实现了节点宕机后,流量的及时转移


自信与骄傲有异:信者常沉着,而骄傲者常浮扬。                                                ——梁启超

🍀🍀🍀🍀🍀🍀🍀🍀🍀🍀🍀🍀🍀🍀🍀🍀🍀🍀🍀🍀🍀🍀🍀🍀🍀🍀🍀🍀

以上,就是本期的全部内容啦,若有错误疏忽希望各位大佬及时指出💐

  制作不易,希望能对各位提供微小的帮助,可否留下你免费的赞呢🌸 

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

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

相关文章

软件设计师-错题笔记-系统开发与运行

1. 解析: A:模块是结构图的基本成分之一,用矩形表示 B:调用表示模块之间的调用关系,通过箭头等符号在结构图中体现 C:数据用于表示模块之间的传递的信息,在结构图中会涉及数据的流向等表示 …

C#简易Modbus从站仿真器

C#使用NModbus库,编写从站仿真器,支持Modbus TCP访问,支持多个从站地址和动态启用/停用从站(模拟离线),支持数据变化,可以很方便实现,最终效果如图所示。 项目采用.net framework 4.…

【深度学习】目标检测算法大全

目录 一、R-CNN 1、R-CNN概述 2、R-CNN 模型总体流程 3、核心模块详解 (1)候选框生成(Selective Search) (2)深度特征提取与微调 2.1 特征提取 2.2 网络微调(Fine-tuning) …

视觉-语言-动作模型:概念、进展、应用与挑战(下)

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

一键解锁嵌入式UI开发——LVGL的“万能配方”

面对碎片化的嵌入式硬件生态,LVGL堪称开发者手中的万能配方。它通过统一API接口屏蔽底层差异,配合丰富的预置控件(如按钮、图表、滑动条)与动态渲染引擎,让工程师无需深入图形学原理,效率提升肉眼可见。 L…

智慧城市综合运营管理系统Axure原型

这款Axure原型的设计理念紧紧围绕城市管理者的需求展开。它旨在打破传统城市管理中信息孤岛的局面,通过统一标准接入各类业务系统,实现城市运营管理信息资源的全面整合与共享。以城市管理者为中心,为其提供一个直观、便捷、高效的协同服务平台…

Qwen智能体qwen_agent与Assistant功能初探

Qwen智能体qwen_agent与Assistant功能初探 一、Qwen智能体框架概述 Qwen(通义千问)智能体框架是阿里云推出的新一代AI智能体开发平台,其核心模块qwen_agent.agent提供了一套完整的智能体构建解决方案。该框架通过模块化设计,将L…

可视化图解算法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基本语法 通常都是&#xff…

Git实战经验分享:深入掌握git commit --amend的进阶技巧

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

PTA:jmu-ds-最短路径

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

WEB UI自动化测试之Pytest框架学习

文章目录 前言Pytest简介Pytest安装Pytest的常用插件Pytest的命名约束Pytest的运行方式Pytest运行方式与unittest对比主函数运行命令行运行执行结果代码说明 pytest.ini配置文件方式运行&#xff08;推荐&#xff09;使用markers标记测试用例 pytest中添加Fixture&#xff08;测…

深入理解 iOS 开发中的 `use_frameworks!`

在使用 CocoaPods 管理 iOS 项目依赖时&#xff0c;开发者经常会在 Podfile 文件中看到一个配置选项&#xff1a;use_frameworks!。本文将详细介绍这个配置选项的含义&#xff0c;以及如何决定是否在项目中使用它。 一、什么是 use_frameworks! 在 CocoaPods 中引入第三方库时…

矩阵置零算法讲解

矩阵置零算法讲解 一、问题描述 给定一个 (m \times n) 的矩阵,如果一个元素为 (0) ,则将其所在行和列的所有元素都设为 (0) 。要求使用原地算法,即在不使用额外矩阵空间的情况下完成操作。 二、解题思路 暴力解法 最直观的想法是遍历矩阵,当遇到 (0) 元素时,直接将其…

二本计算机,毕业=失业?

我嘞个豆&#xff0c;二本计算机&#xff0c;毕业即失业&#xff1f;&#xff01; 今天咱们聊聊普通院校计算机专业的学生未来的发展方向。有些话可能不太中听&#xff0c;但希望大家能理性看待。 首先得承认&#xff0c;对于普通双非和二本的学生来说&#xff0c;就业率加上…

[docker基础二]NameSpace隔离实战

目录 一 实战目的 二 基础知识 1)dd 命令详解 2)mkfs命令详解 3)df命令详解 4)mount 命令详解 5)unshare命令详解 三 实战操作一(PID隔离) 四 实战操作二(MOunt隔离) 1&#xff09;创建 Mount 隔离进程 2&#xff09;在新进程里边&#xff0c;创建空白文件&#…

Day22打卡-复习

复习日 仔细回顾一下之前21天的内容&#xff0c;没跟上进度的同学补一下进度。 作业&#xff1a; 自行学习参考如何使用kaggle平台&#xff0c;写下使用注意点&#xff0c;并对下述比赛提交代码 泰坦尼克号人员生还预测https://www.kaggle.com/competitions/titanic/overview K…

uniapp + vue3 + 京东Nut动作面板组件:实现登录弹框组件(含代码、案例、小程序截图)

uniapp + vue3 + 京东Nut动作面板组件:实现登录弹框组件(含代码、案例、小程序截图) 代码示下,不再赘述。 动作面板组件:https://nutui-uniapp.netlify.app/components/feedback/actionsheet.html 项目背景 业务需求 描述: uniapp + vue3 + 京东Nut框架:实现登录弹框组…

C++类和对象--中阶

C类和对象中阶 01. 类的6个默认成员函数 在 C 中&#xff0c;类有 6 个特殊的默认成员函数&#xff08;不是 6 个构造函数&#xff09;&#xff0c;它们会在特定情况下由编译器自动生成。包括构造函数&#xff0c;析构函数&#xff0c;拷贝构造和赋值运算符重载&#xff0c;取…