前言
其实这部分知识我是整理在语雀上了,这里是直接复制粘贴过来的。不是很好阅读,可以直接点下方链接去语雀看,那个看的会舒服很多。
https://www.yuque.com/g/ngioig/upbg6b/fkarhyo8fpgrtyq8/collaborator/join?token=GvlO0di8KaIfO8aF\&source=doc_collaborator# 《RabbitMQ常见问题知识库》
1.MQ的作⽤及应⽤场景
知识点
消息队列(MQ)是⼀种应⽤程序间的通信⽅法,它允许系统组件以异步的⽅式进⾏交互, 在不同的应⽤场景下可以展现不同的作⽤, 常⻅的应⽤场景如下:
异步解耦: 在业务流程中, ⼀些操作可能⾮常耗时, 但并不需要即时返回结果. 可以借助MQ把这些操作异步化, ⽐如 ⽤⼾注册后发送注册短信或邮件通知, 可以作为异步任务处理, ⽽不必等待这些操作完成后才告知⽤⼾注册成功.
流量削峰: 在访问量剧增的情况下, 应⽤仍然需要继续发挥作⽤, 但是是这样的突发流量并不常⻅. 如果以能处理这类峰值为标准⽽投⼊资源,⽆疑是巨⼤的浪费. 使⽤MQ能够使关键组件⽀撑突发访问压⼒, 不会因为突发流量⽽崩溃. ⽐如秒杀或者促销活动, 可以使⽤MQ来控制流量, 将请求排队, 然后系统根据⾃⼰的处理能⼒逐步处理这些请求.
异步通信: 在很多时候应⽤不需要⽴即处理消息, MQ提供了异步处理机制, 允许应⽤把⼀些消息放⼊MQ中, 但并不⽴即处理它,在需要的时候再慢慢处理.
消息分发: 当多个系统需要对同⼀数据做出响应时, 可以使⽤MQ进⾏消息分发. ⽐如⽀付成功后, ⽀付系统可以向MQ发送消息, 其他系统订阅该消息, ⽽⽆需轮询数据库.
延迟通知: 在需要在特定时间后发送通知的场景中, 可以使⽤MQ的延迟消息功能, ⽐如在电⼦商务平台中,如果⽤⼾下单后⼀定时间内未⽀付,可以使⽤延迟队列在超时后⾃动取消订单
类似问题:
1.项**⽬什么场景下使⽤到了MQ, 为什么需要MQ ?**
- 系统解耦
- 场景 :多个系统之间需要通信,但不想直接耦合(如电商平台的订单系统、库存系统、支付系统)。
- 原因:通过 MQ,生产者(如订单系统)只需发送消息到队列,消费者(如库存系统)独立处理,互不影响。例如,订单生成后发送消息到 MQ,库存系统监听扣减库存,后续新增优惠券系统时无需修改订单系统代码。
- 异步处理
- 场景 :用户注册后需发送短信、邮件等耗时操作。
- 原因:将消息发送到 MQ 后立即返回响应,消费者异步处理任务,提升系统吞吐量。例如,用户点击注册后,系统只需将消息写入 MQ,无需等待短信和邮件发送完成。
- 削峰填谷
- 场景 :秒杀活动、突发流量(如春节抢红包)。
- 原因:高并发请求先存入 MQ,消费者按自身能力处理,避免数据库或服务被瞬间压垮。例如,10 万并发请求进入 MQ,消费者每秒处理 1 万,10 秒完成,避免直接冲击数据库
自己想的: 在消息分发和判题的的时候用到的MQ,系统给所有用户进行消息分发.多个判题避免线程竞争
2.RabbitMQ 的作⽤?使⽤场景有哪些 消息队列的应⽤场景
RabbitMQ 是一款功能强大的开源 MQ,核心功能包括:
- 可靠消息传递:支持持久化、确认机制,确保消息不丢失。
- 灵活路由:通过 Exchange 和 Queue 实现不同消息路由策略(如 Fanout、Direct、Topic)。
- 多语言支持:适配 Java、Python、Go 等主流语言。
主要应用场景:
- 异步任务处理:如用户注册后的短信 / 邮件发送、订单生成后的物流通知。
- 系统解耦:微服务架构中各服务通过 MQ 通信,降低依赖。
- 流量削峰:应对突发流量(如电商大促),平滑请求压力。
- 日志聚合:多个服务将日志发送到 MQ,统一处理存储或分析。
- 事件驱动架构:如订单状态变更触发库存更新、积分发放等连锁反应。
异步解耦,异步通信,流量削峰,消息分发,延迟队列
3.消息队列解耦应⽤程序的例⼦
场景:电商下单流程
- 用户下单后,订单系统将消息(如
order_created
)发送到 MQ。- 库存系统监听该消息,扣减库存。
- 支付系统监听消息,触发支付流程。
- 物流系统监听消息,准备发货。
优势 :若后续新增优惠券系统,只需新增一个消费者监听order_created
消息,无需修改订单系统代码。
用户注册完后,发送注册通知或者短信,不需要同时发完
4.为什么说消息队列可以削峰
四、削峰原理
当大量请求(如 10 万 / 秒)涌入时,直接处理可能导致系统崩溃。MQ 作为中间层:
- 暂存请求:将请求存入队列,消费者按能力处理(如 1 万 / 秒)。
- 平滑流量 :避免瞬间压力过大,保障系统稳定性。
类比:高速公路收费站,车多时先进入匝道排队(MQ),再逐个通过(消费者处理)。
限制流量,放在队列中一个个来,避免一次性接收多个请求导致服务器崩溃
2.了解过哪些MQ,以及他们的区别
知识点
⽬前业界有很多的MQ产品, 例如RabbitMQ, RocketMQ, ActiveMQ, Kafka, ZeroMQ等,
简单介绍其中3种:
- Kafaka
Kafka⼀开始的⽬的就是**⽤于⽇志收集和传输,追求⾼吞吐量, 性能卓越, 单机吞吐达到百万级** , 在⽇志领域⽐较成熟, 功能较为简单, 主要⽀持简单的 MQ 功能. 适合⼤数据处理, ⽇志聚合, 实时分析等场景
- RabbitMQ
采⽤Erlang语⾔开发, MQ 功能⽐较完备, 且⼏乎⽀持所有主流语⾔, 开源提供的界⾯也⾮常友好, 性能较 好, 吞吐量能达到万级, 社区活跃度较⾼,⽂档更新频繁, ⽐较适合中⼩型公司, 数据量没那么⼤, 且并发没那么⾼的场景.
- RocketMQ 采⽤Java语⾔开发, 由阿⾥巴巴开源, 后捐赠给了Apache. 在可⽤性, 可靠性以及稳定性等⽅⾯都⾮常出 ⾊ , 吞吐量能达到⼗万级, 在Alibaba集团内部⼴泛使⽤, 但⽀持的客⼾端语⾔不多, 产品较新⽂档较少, 且社区活跃度⼀般. 适合于⼤规模分布式系统, 可靠性要求⾼, 且并发⼤的场景, ⽐如互联⽹⾦融. 这些消息队列, 各有侧重, 没有好坏, 只有适合不适合, 在实际选型时, 需要结合⾃⾝需求以及MQ产品特 征, 综合考虑
了解过哪些MQ, 与其他同类产品的对⽐
kafka 和 RabbitMQ 和 RocketMQ的对⽐
一、主流 MQ 对比表格
|-----------|--------------|----------------------|----------------|
| 特性 | RabbitMQ | Kafka | RocketMQ |
| 可靠性 | 高(持久化 + ACK) | 高(多副本) | 高(异步刷盘 + 同步复制) |
| 吞吐量 | 万级 TPS | 百万级 TPS | 十万级 TPS |
| 延迟 | 低(ms 级) | 低(ms 级) | 低(ms 级) |
| 消息顺序性 | 严格保证 | 分区内有序 | 严格保证 |
| 分布式事务 | 不支持 | 不支持 | 支持(半消息) |
| 复杂度 | 高(Erlang 依赖) | 中等(需理解分区) | 中等(Java 生态友好) |
| 生态与社区 | 成熟,多语言支持 | 丰富(与 Spark/Flink 集成) | 阿里系,Java 生态 |
kafka追求日志收集和传输,追求性能卓越,吞吐量到达10w级,支持简单的MQ功能,适合大数据处理,日志聚合,实时分析场景.
RabbitMQ采用Erlang语言开发,几乎支持所有主流语言,吞吐量到达万级,MQ功能完备,社区活跃度比较高.比较适合中小型,并发量没那么高,数据量没那么大的公司.
对⽐其他消息队列,不同mq分别⽤在什么场景
- RabbitMQ:可靠性与灵活性兼顾,适合金融、小规模高可靠场景。
- Kafka:吞吐量王者,适合日志、流计算等海量数据场景。
- RocketMQ:分布式事务与高并发平衡,适合电商核心链路。
- 其他:根据场景选择轻量级(ZeroMQ)、云原生(Pulsar)或遗留系统(ActiveMQ)。
|----------------|-------------------|----------------------------------|
| 场景 | 推荐 MQ | 原因 |
| 高可靠性交易 | RabbitMQ/RocketMQ | 支持消息持久化、ACK 机制,RocketMQ 还支持分布式事务 |
| 海量日志 / 流数据 | Kafka | 百万级 TPS 吞吐量,与大数据框架集成成熟 |
| 高并发电商核心链路 | RocketMQ | 支持分布式事务、严格消息顺序,吞吐量十万级 |
| 低延迟通信 | ZeroMQ | 内存型无中间节点,μs 级延迟 |
| 遗留 Java 系统 | ActiveMQ | 传统 Java 生态支持,但性能一般 |
| 云原生场景 | Pulsar | 多租户、持久化、流处理一体化 |
kafka 日志领域,日志收集传输聚合,大数据处理
RabbitMQ 轻量级应用开发,异步解耦通信,可靠性没那么高,数据量没那么大
RocketMQ 用在高可靠性高稳定性以及高并发大量数据的场景,比如互联网金融.
消息队列除了使⽤RabbitMQ,可以⽤RocketMQ吗?
可以,尤其适合:
- 高并发场景(如秒杀、大促);
- 消息顺序性要求严格(如物流状态更新)。
- 需要分布式事务(如订单支付与库存同步);
- 注意:RocketMQ 的部署复杂度高于 RabbitMQ,需结合团队技术栈评估。
可以的,Rocket也支持了MQ的完备功能
3.RabbitMQ的核⼼概念及⼯作流程
RabbitMQ 的核心流程
1. 生产者发送消息
- 创建消息:生产者应用程序根据业务需求创建消息内容,消息可以是文本、JSON 数据、二进制数据等,同时会为消息指定一些属性,如消息的优先级、过期时间等。
- 连接到 Broker:生产者需要与 RabbitMQ 的 Broker(服务器实例)建立 TCP 连接,之后在该连接上创建一个或多个信道(Channel),信道是进行消息通信的逻辑通道,使用信道而不是直接使用 TCP 连接可以减少系统开销。
- 指定交换机和路由键:生产者将消息发送到指定的交换机,并为消息指定路由键(routing key)。路由键是一个字符串,用于交换机根据其类型和绑定规则来决定如何将消息路由到队列。
2. 交换机路由消息
- 接收消息:交换机接收到生产者发送的消息。
- 匹配绑定规则:交换机根据自身的类型(如直连交换机、扇形交换机、主题交换机、头交换机)和绑定规则,结合消息的路由键,决定将消息路由到哪些队列。例如,直连交换机根据路由键和绑定键的精确匹配来路由消息;扇形交换机则将消息广播到所有与之绑定的队列。
- 转发消息:交换机将消息转发到匹配的队列中。
3. 队列存储消息
- 接收消息:队列接收到交换机转发过来的消息,并将其存储在队列中。
- 持久化处理(可选):如果队列设置为持久化(durable),消息会被存储在磁盘上,以防止 Broker 重启时消息丢失。
4. 消费者接收消息
- 连接到 Broker:消费者同样需要与 RabbitMQ 的 Broker 建立 TCP 连接,并创建信道。
- 订阅队列:消费者向 Broker 表明自己要监听的队列,通过信道订阅该队列。
- 接收消息:当队列中有新消息时,Broker 会将消息推送给订阅该队列的消费者(推模式),或者消费者主动从队列中拉取消息(拉模式)。
- 消息确认:消费者接收到消息并处理完成后,会向 Broker 发送确认信息(ACK),告知 Broker 该消息已成功处理,Broker 收到确认后会将消息从队列中删除。如果消费者在处理消息过程中出现异常,没有发送确认信息,Broker 会认为消息处理失败,可能会将消息重新发送给其他消费者或进行其他处理。
讲讲RabbitMQ的结构
RabbitMQ 的结构可以从逻辑结构 和物理结构两个层面来理解。
逻辑结构组件
- 生产者(Producer):负责创建并发送消息到 RabbitMQ 的应用程序。例如,在一个电商系统中,订单服务在用户下单后会作为生产者将订单消息发送到 RabbitMQ。
- 消费者(Consumer):从 RabbitMQ 接收消息并进行处理的应用程序。如库存服务会作为消费者监听与订单相关的队列,当接收到订单消息后进行库存扣减操作。
- 交换机(Exchange):是消息的接收和路由中心,根据不同的类型和绑定规则将消息路由到队列。常见的交换机类型有直连交换机、扇形交换机、主题交换机和头交换机。
- 队列(Queue):用于存储消息的缓冲区,是消息的最终目的地。队列可以设置多种属性,如持久化、排他性、自动删除等。
- 绑定(Binding):建立交换机和队列之间的关联关系,通过绑定键来实现。它决定了交换机如何将消息路由到队列。
物理结构组件
- 虚拟主机(Virtual Host):是一个逻辑分组概念,为不同的用户或应用程序提供独立的环境。每个虚拟主机都有自己独立的交换机、队列和绑定,相互隔离,提高了系统的安全性和可管理性。
- Broker:RabbitMQ 服务器的实例,是整个消息队列系统的核心。它负责管理交换机、队列、绑定等组件,处理消息的收发和存储,同时提供监控、管理等功能。一个 Broker 可以包含多个虚拟主机。
4.RabbitMQ如何保证消息的可靠性
RabbitMQ消息丢失原因及其解决⽅案
|------------------|----------------------------------|------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
| 消息丢失阶段 | 原因 | 解决方案 |
| 生产者发送消息时 | 网络问题、Broker 故障等导致消息未成功发送到 Broker | 1. 事务机制 :生产者开启事务,若消息发送失败,事务回滚并重试。但这种方式会严重影响性能,因为它是同步阻塞的。例如在 Java 中使用 channel.txSelect()
开启事务,channel.txCommit()
提交事务,channel.txRollback()
回滚事务。 2. 确认机制(Confirm 模式) :推荐使用异步确认模式,生产者发送消息后,Broker 会返回确认信息。若未收到确认,生产者可以进行重试。如在 Java 中使用 channel.confirmSelect()
开启 Confirm 模式,通过 channel.addConfirmListener()
监听确认结果。 |
| 消息在交换机中无法路由到指定队列 | 可能原因是代码或者配置层面错误,导致消息路由失败, | 解决办法是参考发送方确认
- return模式****。 |
| Broker 存储消息时 | 磁盘故障、Broker 崩溃等导致消息丢失 | 1. 队列和消息持久化 :声明队列时设置 durable
为 true
,同时将消息的 deliveryMode
设置为 2,表示持久化消息。这样即使Broker 重启,消息也不会丢失。 2. 镜像队列:在 RabbitMQ 集群中配置镜像队列,将消息复制到多个节点,提高消息的可靠性。当主节点故障时,从节点可以继续提供服务。 |
| 消费者消费消息时 | 消费者处理消息过程中出现异常、网络中断等导致消息未正确处理 | 1. 手动确认机制(Manual Acknowledgment) :消费者在处理完消息后,手动发送 ACK 给 Broker,告知 Broker 消息已成功处理。若消费者未发送 ACK 就断开连接,Broker 会将消息重新分配给其他消费者。 2. 死信队列(Dead Letter Queue,DLQ):当消息消费失败多次后,可以将消息发送到死信队列,后续进行人工处理或重试。 |
如何保证消息不丢失
要保证消息不丢失,需要从生产者、Broker 和消费者三个方面进行综合处理:
- 生产者端 :**采用确认机制或者事务,**确保消息成功发送到 Broker。
- Broker 端 :使用队列和消息持久化,以及镜像队列等方式,保证消息在 Broker 存储期间的可靠性。
- 消费者端 :使用手动确认机制,只有在消息处理成功后才发送 ACK。 死信队列(Dead Letter Queue,DLQ):当消息消费失败多次后,可以将消息发送到死信队列,后续进行人工处理或重试。
消息写⼊失败怎么办
- 重试机制:如果生产者发现消息写入失败(如未收到 Broker 的确认信息),可以进行重试。为了避免无限重试,可以设置最大重试次数和重试间隔时间。
- 记录日志:将写入失败的消息记录到日志中,方便后续排查问题。
- 补偿机制:对于一些重要的消息,放到死信队列通过其他方式(如人工干预)进行补偿。
消息消费失败如何处理
- 重试机制:消费者在处理消息失败时,可以进行重试。可以设置重试次数和重试间隔时间,避免无限重试。
- 死信队列:当消息重试多次仍然失败时,将消息发送到死信队列,后续进行人工处理或分析失败原因。
- 记录日志:记录消费失败的消息和错误信息,方便后续排查问题。
MQ的主动ack和被动ack有什么区别
- 主动 ack(手动确认) :消费者在处理完消息后,手动调用
basicAck
方法向 Broker 发送确认信息。这种方式可以确保消息在消费者真正处理完成后才被标记为已消费,提高了消息处理的可靠性。但需要开发者自己处理确认逻辑,增加了代码复杂度。 - 被动 ack(自动确认):消费者在接收到消息后,Broker 会自动认为消息已被消费,无需消费者手动发送确认信息。这种方式简单方便,但存在一定的风险,例如消费者在处理消息过程中出现异常,消息可能会丢失。
RabbitMQ如何解决数据丢失问题, 如何保证⼀致性
- 解决数据丢失问题:通过上述提到的生产者确认机制、Broker 持久化和镜像队列、消费者手动确认机制等方式,确保消息在整个生命周期内不丢失。
- 保证一致性:
-
- 最终一致性:允许系统在一段时间内存在数据不一致的情况,但最终会达到一致。例如,在分布式系统中,通过消息队列进行异步通信,各个服务可能在不同的时间点处理消息,但最终会达成一致的状态。
- 分布式事务:结合 RabbitMQ 和分布式事务框架(如 Seata),确保在消息发送和业务处理过程中的一致性。例如,在电商系统中,订单服务发送消息到 RabbitMQ 通知库存服务扣减库存,同时使用分布式事务保证订单状态和库存数量的一致性。
消息队列怎么保证消费者的消息不丢失的?
保证消费者消息不丢失的核心是手动 ACK + 健壮的消费逻辑 ,结合持久化 和限流 提升可靠性。若出现异常,通过死信队列 和重试机制兜底,最终实现消息的可靠消费。
5.RabbitMQ如何保证消息的顺序性
RabbitMQ怎么保证消息的顺序性?
如何保证消息能够有序消费
1. 单队列单消费者模式
- 将所有消息发送到同一个队列
- 使用单个消费者绑定该队列
- 优点:天然保证消息顺序性
- 缺点:性能瓶颈,无法水平扩展
2. 路由键分组策略
- 按业务维度设计路由键(如 user_id)
- 相同路由键的消息路由到同一队列
- 每个队列配置独立消费者组
- 示例:订单消息按 user_id 哈希路由到不同队列
3. 消费者端处理
- 保持消息处理逻辑的幂等性
- 使用本地缓存维护消息顺序(如 Redis 有序集合)
- 实现重试机制时保持顺序重试
- 处理耗时操作时使用异步线程池但保证结果合并顺序
4. 生产者保障
- 使用事务或 publisher confirm 模式确保消息发送顺序
- 批量发送时保持批次内顺序
- 避免跨线程并发发送同一业务线消息
5. 高级特性组合
- 结合死信队列处理异常消息
- 使用优先级队列保障关键消息优先
- 配合消息时间戳实现延迟队列顺序消费
实现关键点:
- 顺序性与吞吐量是对立指标,需根据业务需求权衡
- 建议采用业务逻辑分片(如分库分表策略)来实现局部有序
- 监控队列积压情况,避免消息堆积导致顺序混乱
- 定期校验消息序号连续性(可通过消息头携带 Sequence ID)
实际应用中,可根据业务场景选择组合方案。例如电商订单系统可采用路由键分组 + 单消费者队列的方式,在保证订单维度消息顺序的同时,通过多队列实现整体吞吐量提升。
6.如何保证消息消费时的幂等性
消息或请求存在重复消费问题吗? RabbitMQ怎么保证消息不重复消费
RabbitMQ 可能会出现消息重复消费的问题,比如在消费者从 RabbitMQ 获取消息后,还未完成处理就发生了网络故障或其他异常,导致 RabbitMQ 认为消息没有被正确处理而重新投递,就可能造成重复消费。以下是一些保证消息不重复消费的方法:
1. 利用消息的唯一标识
a. 在消息生产者端:为每条消息生成一个全局唯一的标识,例如使用 UUID。将这个唯一标识与消息一起发送到 RabbitMQ。
b. 在消息消费者端:消费者在处理消息前,先检查该消息的唯一标识是否已经被处理过。可以使用数据库、缓存(如 Redis)等来记录已经处理过的消息标识。当收到一条新消息时,首先查询数据库或缓存,看是否已经存在该标识。如果存在,说明是重复消息,直接丢弃;如果不存在,则进行正常的消息处理流程,并在处理完成后将该消息标识存入数据库或缓存。
2. 基于数据库的事务机制
a. 在消息消费者端:将消息处理逻辑和消息状态更新放在同一个数据库事务中。例如,在处理一条订单消息时,将订单数据的插入或更新操作与记录消息已处理的操作放在一个事务中。如果消息处理成功,事务提交,消息状态更新为已处理;如果处理过程中出现异常,事务回滚,消息状态仍为未处理,RabbitMQ 可以重新投递消息。这样可以保证消息要么被成功处理并记录为已处理,要么不做任何更改等待重新处理,避免了重复处理的情况。
3. 利用消息队列的确认机制
a. 在消息消费者端 :消费者在成功处理完消息后,再向 RabbitMQ 发送确认消息。RabbitMQ 只有收到确认消息后才会将消息从队列中删除。如果消费者在处理消息过程中出现故障,没有发送确认消息,RabbitMQ 会认为消息没有被成功处理,会重新投递消息。通过这种方式,确保每条消息都能被正确处理且仅处理一次。不过需要注意在消费者处理消息时间较长的情况下,可能需要合理设置 RabbitMQ 的相关参数,如heartbeat
等,以避免被 RabbitMQ 误认为消费者已失联而重新投递消息。
4. 幂等性处理
a. 在业务逻辑层面 :设计业务逻辑时,使业务操作具有幂等性,即多次执行相同的操作,结果与执行一次相同。以更新用户积分的操作为例,假设消息内容是给用户 ID 为 123 的用户增加 10 积分。在数据库中,可以使用UPDATE user_score SET score = score + 10 WHERE user_id = 123
这样的语句来更新积分,无论这条消息被消费多少次,用户的积分只会增加一次。这样即使出现消息重复消费,也不会对业务数据造成影响。
7.RabbitMQ有哪些特性
RabbitMQ的死信队列以及应⽤场景
死信队列的概念
当消息在 RabbitMQ 中成为死信(Dead Letter)时,它会被重新发送到一个特定的队列,这个队列就是死信队列。消息成为死信的情况通常有以下几种:
- 消息被拒绝 :消费者使用
basic.reject
或basic.nack
方法拒绝消息,并且没有设置requeue=true
,那么该消息会成为死信。- 消息过期 :通过设置队列的
x-message-ttl
参数或消息的expiration
属性,当消息在队列中停留的时间超过了设置的过期时间,就会变成死信。- 队列达到最大长度 :当队列设置了
x-max-length
或x-max-length-bytes
参数,达到了最大长度或最大字节数时,新进入队列的消息会被丢弃或成为死信,取决于具体配置。
死信队列的工作原理
一般来说,要使用死信队列,需要先声明一个正常的队列和一个死信队列,以及相应的交换机。正常队列通过设置x-dead-letter-exchange
和x-dead-letter-routing-key
参数来指定死信交换机和路由键。当正常队列中的消息成为死信后,会根据设置的死信交换机和路由键被发送到死信队列中,后续可以对死信队列中的消息进行相应处理,比如记录日志、重新发送等。
死信队列的应用场景
- 消息重试机制:当消息消费失败时,可以先将消息发送到死信队列。在死信队列的消费者中,可以根据一定的策略对消息进行重试,例如设置重试次数、重试间隔等。如果重试一定次数后仍然失败,可以将消息记录下来或者进行其他特殊处理,避免消息丢失。
- 数据清理与补偿:在一些数据同步或批量处理的场景中,如果部分消息处理失败成为死信,可以从死信队列中获取这些消息,分析失败原因,进行数据清理或者补偿操作,以保证数据的一致性和完整性。
- 监控与告警:通过监控死信队列中的消息数量、类型等信息,可以及时发现系统中可能存在的问题,比如消费者出现故障导致大量消息积压成为死信,或者某些特定类型的消息频繁出现消费失败等情况。可以根据死信队列的状态设置告警机制,及时通知运维人员或开发人员进行处理。
- 异步任务超时处理:在异步任务处理中,有些任务可能有执行时间限制。例如,一个订单创建后,如果在一定时间内没有收到支付成功的消息,就可以将订单相关的消息发送到死信队列,然后根据死信队列中的消息来进行订单超时取消等操作。
- 异常流量控制:当系统遇到突发的异常流量或者异常消息时,正常队列可能会被大量无效或异常消息填满。通过死信队列可以将这些异常消息隔离出来,避免它们影响正常业务流程的处理,同时可以对死信队列中的异常消息进行分析,找出流量异常的原因并采取相应的措施。
8.介绍下RabbitMQ的延迟队列
1. 概念
RabbitMQ 延迟队列是一种允许消息在指定的时间之后才被消费者消费的特殊队列。在常规的消息队列中,消息一旦被发送到队列,消费者就可以立即获取并处理。而延迟队列打破了这种即时性,它可以让消息在设定的延迟时间到达后才进入可消费状态,使得消息的处理在时间上具有一定的延迟性。
2. 应用场景
- 订单超时处理:在电商系统中,当用户创建订单后,如果在一定时间内(如 30 分钟)没有完成支付,系统需要自动取消该订单。可以将订单消息发送到延迟队列,设置延迟时间为 30 分钟,30 分钟后消息出队,系统根据消息内容取消对应的订单。
- 缓存预热:对于一些经常被访问的数据,为了提高系统的响应速度,可以在系统启动后一段时间或者特定时间点,将数据的预热任务消息发送到延迟队列。当延迟时间到达,消息被消费,触发缓存预热操作,提前将数据加载到缓存中。
- 消息重试机制:当消息消费失败时,不立即进行重试,而是将消息发送到延迟队列,设置一定的延迟时间(如 5 分钟)后再进行重试。这样可以避免因网络抖动等短暂问题导致的频繁重试,同时也给系统一定的恢复时间。
- 定时提醒:在社交、办公等应用中,需要给用户发送定时提醒消息。例如,会议开始前 15 分钟提醒参会人员,将提醒消息发送到延迟队列,设置延迟时间为 15 分钟,时间到达后消息出队并发送提醒给用户。
3. 实现方式
利用 TTL(Time-To-Live)和死信队列
- 原理:TTL 是指消息的存活时间,RabbitMQ 允许为队列或消息单独设置 TTL。当消息的 TTL 到期后,如果消息所在的队列配置了死信交换机和路由键,消息会被发送到死信队列。通过设置消息的 TTL 来实现延迟效果,当消息到达死信队列时,消费者从死信队列中消费消息,从而达到延迟消费的目的。
- 实现步骤
- 声明正常队列和死信队列 :创建一个正常队列,并为其设置
x-dead-letter-exchange
和x-dead-letter-routing-key
参数,指定死信交换机和路由键。同时声明死信队列和对应的死信交换机。- 设置消息 TTL :在发送消息时,通过设置消息的
expiration
属性或者在队列上设置x-message-ttl
参数来指定消息的存活时间。- 消费者监听死信队列:消费者监听死信队列,当消息因 TTL 到期进入死信队列后,消费者进行消费。
java
import com.rabbitmq.client.Channel;
import com.rabbitmq.client.Connection;
import com.rabbitmq.client.ConnectionFactory;
import java.io.IOException;
import java.util.HashMap;
import java.util.Map;
import java.util.concurrent.TimeoutException;
public class DelayQueueExample {
private static final String NORMAL_EXCHANGE = "normal_exchange";
private static final String NORMAL_QUEUE = "normal_queue";
private static final String DEAD_LETTER_EXCHANGE = "dead_letter_exchange";
private static final String DEAD_LETTER_QUEUE = "dead_letter_queue";
public static void main(String[] args) throws IOException, TimeoutException {
ConnectionFactory factory = new ConnectionFactory();
factory.setHost("localhost");
Connection connection = factory.newConnection();
Channel channel = connection.createChannel();
// 声明死信交换机和死信队列
channel.exchangeDeclare(DEAD_LETTER_EXCHANGE, "direct");
channel.queueDeclare(DEAD_LETTER_QUEUE, true, false, false, null);
channel.queueBind(DEAD_LETTER_QUEUE, DEAD_LETTER_EXCHANGE, "routing_key");
// 声明正常交换机和正常队列,并设置死信交换机和路由键
Map<String, Object> argsMap = new HashMap<>();
argsMap.put("x-dead-letter-exchange", DEAD_LETTER_EXCHANGE);
argsMap.put("x-dead-letter-routing-key", "routing_key");
channel.exchangeDeclare(NORMAL_EXCHANGE, "direct");
channel.queueDeclare(NORMAL_QUEUE, true, false, false, argsMap);
channel.queueBind(NORMAL_QUEUE, NORMAL_EXCHANGE, "routing_key");
// 发送消息并设置 TTL
String message = "Delayed message";
channel.basicPublish(NORMAL_EXCHANGE, "routing_key", null, message.getBytes());
channel.basicPublish(NORMAL_EXCHANGE, "routing_key", null, message.getBytes());
System.out.println(" [x] Sent '" + message + "'");
channel.close();
connection.close();
}
}
使用 RabbitMQ 插件(rabbitmq_delayed_message_exchange)
- 原理:该插件为 RabbitMQ 提供了专门的延迟消息交换器类型。通过使用这个插件,可以直接在交换机层面实现消息的延迟,而不需要依赖 TTL 和死信队列的组合。
- 实现步骤
- 安装插件 :将
rabbitmq_delayed_message_exchange
插件安装到 RabbitMQ 服务器上,并启用该插件。- 声明延迟交换机 :在代码中声明一个类型为
x-delayed-message
的交换机。- 发送消息并设置延迟时间 :在发送消息时,通过设置消息的
x-delay
属性来指定延迟时间,单位为毫秒。- 消费者监听队列:消费者监听与延迟交换机绑定的队列,当延迟时间到达后,消息会被发送到队列,消费者进行消费。
介绍下RabbitMQ的⼯作模式
看下边的博客
RabbitMQ 7种工作模式详解及应用场景_rabbitmq 广播模式应用场景-CSDN博客
10.消息积压的原因, 如何处理
RabbitMQ 消息积压是指在消息队列中,消息的产生速度远远大于消息的消费速度,导致大量消息在队列中堆积,不能及时被处理。以下是消息积压的常见原因及处理方法:
消息积压的原因
- 消费者处理能力不足:消费者的消费速度跟不上生产者的生产速度。可能是因为消费者的业务逻辑复杂,处理每条消息需要耗费较长时间,也可能是消费者的资源(如 CPU、内存、网络等)有限,无法处理大量的消息。
- 生产者发送消息速度过快:生产者在短时间内产生了大量的消息并发送到 RabbitMQ 中,超过了 RabbitMQ 和消费者的处理能力。
- 网络问题:网络不稳定、延迟高或出现短暂中断等情况,会导致消息发送或消费过程受阻。例如,消费者与 RabbitMQ 之间的网络连接不稳定,可能会导致消息确认延迟,使得 RabbitMQ 不能及时将消息标记为已消费,从而造成消息积压。
- RabbitMQ 配置不合理:如队列的容量设置过小,无法容纳大量的消息;或者内存、磁盘等资源限制设置不合理,当消息量达到一定程度时,RabbitMQ 性能下降,导致消息处理速度变慢。
- 消费者故障或异常:消费者程序出现故障、崩溃或陷入死循环等问题,无法正常消费消息。如果没有及时发现并修复,就会导致消息不断积压。
- 消息重试机制问题:当消息消费失败时,如果重试机制设置不合理,例如重试间隔过短、重试次数过多等,可能会导致大量消息在重试过程中积压。
处理消息积压的方法
- 增加消费者数量:可以通过增加消费者的实例数量来提高消费能力,使消息能够更快地被处理。可以根据消息积压的程度和系统资源情况,合理地增加消费者数量。
- 优化消费者性能:对消费者的业务逻辑进行优化,减少处理每条消息的时间。例如,优化数据库查询语句、减少不必要的计算和操作等。同时,确保消费者有足够的资源(如 CPU、内存等)来处理消息,可以考虑对消费者所在的服务器进行升级或扩容。
- 限流生产者:如果是生产者发送消息速度过快导致积压,可以对生产者进行限流,控制消息的发送速度,使其与消费者的处理能力相匹配。可以使用令牌桶算法、漏桶算法等限流算法来实现。
- 检查和优化网络:检查网络设备和连接,确保网络稳定。可以增加网络带宽、优化网络拓扑结构等,减少网络延迟和丢包,提高消息发送和消费的效率。
- 调整 RabbitMQ 配置:根据实际情况合理调整 RabbitMQ 的配置参数。例如,增加队列的容量,调整内存和磁盘的使用限制等,以提高 RabbitMQ 处理消息的能力。
- 死信队列处理:可以将积压的消息转移到死信队列中,然后对死信队列中的消息进行单独处理。分析消息积压的原因,针对不同情况采取相应的措施,如修复消费者故障、调整业务逻辑等,再从死信队列中重新消费这些消息。
- 消息清理和补偿:如果积压的消息中有一些是可以丢弃的(如过期的消息、不重要的重复消息等),可以考虑进行清理。对于一些需要保证数据完整性的消息,可以在处理完积压消息后,进行数据补偿操作,确保数据的一致性。
11.RabbitMQ是推模式还是拉模式
RabbitMQ 同时支持推模式(Push)和拉模式(Pull),以下为你详细介绍这两种模式:
推模式(Push)
原理
在推模式下,当 RabbitMQ 中的队列有新消息时,RabbitMQ 服务器会主动将消息推送给消费者。消费者在启动时会与 RabbitMQ 建立连接,并声明要消费的队列,然后通过特定的 API 告知 RabbitMQ 自己准备好接收消息。RabbitMQ 会根据消费者的配置和队列情况,将消息推送给消费者。
java
import com.rabbitmq.client.*;
import java.io.IOException;
import java.util.concurrent.TimeoutException;
public class PushConsumer {
private final static String QUEUE_NAME = "push_queue";
public static void main(String[] args) throws IOException, TimeoutException {
ConnectionFactory factory = new ConnectionFactory();
factory.setHost("localhost");
Connection connection = factory.newConnection();
Channel channel = connection.createChannel();
channel.queueDeclare(QUEUE_NAME, false, false, false, null);
System.out.println(" [*] Waiting for messages. To exit press CTRL+C");
DeliverCallback deliverCallback = (consumerTag, delivery) -> {
String message = new String(delivery.getBody(), "UTF-8");
System.out.println(" [x] Received '" + message + "'");
};
channel.basicConsume(QUEUE_NAME, true, deliverCallback, consumerTag -> { });
}
}
特点
- 实时性高:消息一旦到达队列,会立即被推送给消费者进行处理,能及时响应新消息的到来。
- 减少消费者的轮询开销:消费者无需主动去询问队列是否有消息,降低了消费者的资源消耗。
- 可能导致消费者压力大:如果生产者发送消息的速度过快,而消费者处理能力有限,可能会使消费者不堪重负,出现消息积压或处理不及时的情况。
拉模式(Pull)
原理
在拉模式下,消费者需要主动向 RabbitMQ 发送请求来获取消息。消费者通过调用特定的 API 从队列中拉取消息,每次拉取操作可以指定拉取的消息数量。这种模式下,消费者对消息的获取具有更大的控制权。
java
import com.rabbitmq.client.*;
import java.io.IOException;
import java.util.concurrent.TimeoutException;
public class PullConsumer {
private final static String QUEUE_NAME = "pull_queue";
public static void main(String[] args) throws IOException, TimeoutException {
ConnectionFactory factory = new ConnectionFactory();
factory.setHost("localhost");
Connection connection = factory.newConnection();
Channel channel = connection.createChannel();
channel.queueDeclare(QUEUE_NAME, false, false, false, null);
GetResponse response = channel.basicGet(QUEUE_NAME, true);
if (response != null) {
String message = new String(response.getBody(), "UTF-8");
System.out.println(" [x] Received '" + message + "'");
} else {
System.out.println(" [x] No message available");
}
channel.close();
connection.close();
}
}
特点
- 消费者可控性强:消费者可以根据自身的处理能力和业务需求,灵活地决定何时拉取消息以及拉取多少消息,避免了因消息推送过快而导致的处理压力过大问题。
- 实时性较差:消费者需要主动去拉取消息,如果拉取间隔设置不合理,可能会导致消息处理不及时,存在一定的延迟。
- 增加了消费者的实现复杂度:消费者需要实现轮询拉取消息的逻辑,相比推模式,代码实现更为复杂。
模式选择建议
- 推模式:适用于对消息实时性要求较高、消费者处理能力较强且稳定的场景,例如实时日志处理、实时监控数据处理等。
- 拉模式:适用于消费者处理能力不稳定、需要根据自身负载动态调整消息获取速度的场景,或者对消息处理的顺序和时机有严格要求的场景。、
整理不易,有帮助可以点个赞,感谢阅览!!