不管是初学者还是面试都必看RabbitMQ(AMQP)宝典
引言
关于RabbitMQ这里应有尽有,你可以了解到RabbitMQ的架构组成,四种路由、五种工作模式、应答机制、重试机制、延时队列的构建、以及消息丢失、消息积压、消息重复消费、保证消息顺序等一系列RabbitMQ相关问题的解决方案🎉🎉🎉各位少侠觉得有用一箭三连哦🤞
1 RabbitMQ架构
- Broker:接收和分发消息的应用,RabbitMQ Server就是Message Broker
- Connection: publisher / consumer和 broker之间的TCP连接
- Channel:如果每一次访问RabbitMQ都建立一个Connection,在消息量大的时候建立TCPConnection的开销将是巨大的,效率也较低。Channel是在connection 内部建立的逻辑连接,如果应用程序支持多线程,通常每个thread创建单独的channel进行通讯,AMQP method包含了channel id 帮助客户端和message broker识别 channel,所以channel 之间是完全隔离的。Channel作为轻量级的Connection极大减少了操作系统建TCP connection的开销
- Exchange: message 到达 broker 的第一站,根据分发规则,匹配查询表中的 routing key,分发消息到queue 中去。常用的类型有: direct (point-to-point), topic(publish-subscribe) and fanout(multicast)
- **Routing Key:**生产者将消息发送到交换机时会携带一个key,来指定路由规则
- binding Key:在绑定Exchange和Queue时,会指定一个BindingKey,生产者发送消息携带的RoutingKey会和bindingKey对比,若一致就将消息分发至这个队列
- vHost 虚拟主机:每一个RabbitMQ服务器可以开设多个虚拟主机每一个vhost本质上是一个mini版的RabbitMQ服务器,拥有自己的 “交换机exchange、绑定Binding、队列Queue”,更重要的是每一个vhost拥有独立的权限机制,这样就能安全地使用一个RabbitMQ服务器来服务多个应用程序,其中每个vhost服务一个应用程序。
2 为什么用RabbitMQ
RabbitMQ 是一个开源的消息代理软件,广泛应用于构建可靠、弹性和可扩展的分布式系统。使用 RabbitMQ 有以下几个主要优势:
- 异步通信:RabbitMQ 使用消息队列的方式实现异步通信,提供了一种解耦的方式来处理不同组件之间的通信。通过将消息发送到队列中,发送者和接收者可以独立于彼此进行工作,不需要立即相互通信。
- 高度可靠:RabbitMQ 提供了持久化消息的机制,可以确保即使在发生故障时消息不会丢失。消息可以被持久化到磁盘上,并且在发生故障后可以重新发送。
- 可扩展性:RabbitMQ 支持在多个节点之间进行消息的传递和负载平衡。通过添加更多的节点,可以轻松地扩展系统的处理能力。
- 灵活的消息路由:RabbitMQ 提供了灵活的消息路由机制,可以根据消息的内容和属性将消息发送到不同的队列。这样可以更好地控制消息的流向和处理逻辑。
- 多语言支持:RabbitMQ 提供了多种客户端库,支持多种编程语言,如Java、Python、Ruby和.NET等。这使得开发者可以使用自己熟悉的语言来与消息代理进行通信。
3 RabbitMQ的四种路由
-
直连路由(Direct)
匹配路由键,只有完全匹配消息才会被转发
-
扇形广播路由(Fanout)
将消息发送至所有的队列
-
主题路由(Topic)
将路由按模式匹配,此时队列需要绑定要一个模式上。符号“#”匹配一个或多个词,符号"星号"匹配至多一个词。因此“abc.#”能够匹配到“abc.def.ghi”,但
是“abc.*” 只会匹配到“abc.def”。
-
消息头路由(Headers)
在绑定Exchange和Queue的时候指定一组键值对,header为键,根据请求消息中携带的header进行路由
4 🌟RabbitMQ的五种工作模式
1 simple (简单模式)
一个消费者消费一个生产者生产的信息
2 Work queues(工作模式)
一个生产者生产信息,多个消费者进行消费,但是一条消息只能消费一次
3.Publish/Subscribe(发布订阅模式)
生产者首先投递消息到交换机,订阅了这个交换机的队列就会收到生产者投递的消息
4.Routing路由模式)
生产者生产消息投递到direct交换机中,扇出交换机会根据消息携带的routing Key匹配相应的队列
5.Topics主题模式)
生产者生产消息投递到topic交换机中,上面是完全匹配路由键,而主题模式是模糊匹配,只要有合适规则的路由就会投递给消费者
5 RabbitMQ的应答机制
RabbitMQ的ACK(Acknowledgment)机制是指消费者收到消息后,向RabbitMQ服务器发送确认消息,告知服务器该消息已经被成功处理。ACK机制的实现可以通过以下两种方式来完成:
1 自动ACK(Automatic Acknowledgment)
- 在自动ACK模式下,一旦消息被消费者接收到,RabbitMQ会立即将该消息标记为已经传递,并且不再跟踪这条消息的状态。消费者无需手动发送确认消息,RabbitMQ会自动确认消息的传递。这种方式适用于不需要考虑消息处理成功与否的简单场景。
2 🌟手动ACK(Manual Acknowledgment)
- 在手动ACK模式下,消费者需要在成功处理消息后,显式地发送确认消息给RabbitMQ服务器。这种方式确保了消息在被消费者处理后得到确认,避免了消息丢失的风险。手动ACK机制通常用于需要确保消息可靠传递的场景,比如在消息处理过程中可能发生错误,需要处理失败的消息等情况。
手动ACK机制的步骤如下:
- 消费者从队列中获取消息。
- 消费者处理消息。
- 如果消息处理成功,发送ACK确认消息给RabbitMQ服务器。
- 如果消息处理失败,可以选择拒绝消息(Reject)或者将消息重新放回队列(Nack)。
6 🌟RabbitMQ的重试机制
1. 消息重试基本原理
RabbitMQ的消息重试机制基于以下原则:
- 消费者从队列中获取消息。
- 消费者尝试处理消息。
- 如果消息处理失败,将消息重新发送到队列,以供后续的重试。
2 设置重试次数
为了避免无限循环重试,我们通常会设置一个重试次数上限。这一上限可以通过在消息的属性中添加一个计数器来实现。每次消息处理失败时,计数器递增,当计数器达到设定的重试次数上限时,停止重试,将消息标记为失败或记录错误信息。
3 使用延迟队列(Delay Queue)
有时,我们需要实现消息在一定时间后进行重试。这时,可以使用RabbitMQ的延迟队列,或称为延迟交换机。消息不会立即重新放回原始队列,而是被发送到延迟队列,然后在一段时间后再次路由到原始队列,实现重试。
4 记录错误信息
记录错误信息对于排查问题非常关键。错误信息可以被写入日志文件、发送到监控系统或存储在专用的错误队列中,以供进一步分析和调查。
5 死信队列(Dead-Letter Queue)
如果消息达到了设定的重试次数上限,无法成功处理,可以将其标记为死信,并将其发送到一个死信队列中,以供后续处理或审查。这有助于避免无限重试的问题。
6 消息幂等性
为确保重试不引发不良影响,消息处理逻辑应该是幂等的。这意味着多次处理相同的消息应该产生相同的结果,而不会导致额外的副作用。
7🌟RabbitMQ的延时队列
RabbitMQ本身没有延迟队列,需要靠TTL和DLX模拟出延迟的效果
-
创建两个交换机:
- 创建一个普通交换机(例如:normal_exchange)用于发布消息。
- 创建一个死信交换机(例如:dlx_exchange)用于接收过期消息。
-
创建两个队列:
- 创建一个普通队列(例如:normal_queue),将其绑定到普通交换机。
- 创建一个死信队列(例如:dlx_queue),将其绑定到死信交换机。
-
设置普通队列的死信参数:
- 在普通队列的参数中,设置x-dead-letter-exchange为dlx_exchange,指定死信交换机为dlx_exchange。
-
发布消息:
- 将消息发送到普通交换机,并指定routing_key为普通队列的名称。
- 设置消息的过期时间为延迟时间。
-
处理延迟消息:
- 当消息过期后,会被发送到dlx_exchange,并被绑定到死信队列dlx_queue。
- 消费死信队列中的消息,并进行相应的处理逻辑。
8 🌟RabbitMQ的消息丢失问题
1 生产者消息丢失
- 发送消息时未确认:生产者在发送消息后未等待 RabbitMQ 的确认回执,导致消息可能在传递过程中丢失。解决方案是启用发布者确认模式(Publisher Confirms),确保消息被正确接收和处理。
- 消息过期或者被拒绝:如果消息的 TTL(Time-To-Live)过期或者被 RabbitMQ 拒绝,消息将会被丢弃。生产者要确保消息的 TTL 设置合理,并且处理 RabbitMQ 返回的确认信息和错误信息。
2 队列消息丢失
- 队列被删除:如果消费者消费一个队列,并在消费完成后删除了队列,那么队列中未被消费的消息将会丢失。解决方案是确保在消费者消费队列之前,先声明队列,并且保持消费者在线。
- 队列溢出:如果队列的长度限制超过了设置的阈值,新的消息将会被丢弃。解决方案是监控队列长度,并根据需要设置合理的队列容量限制。
3 消费者消息丢失
- 消费者未确认消息:如果消费者在处理消息后未发送确认消息给 RabbitMQ,RabbitMQ 将认为消息未被正确处理并重新投递给其他消费者。如果没有其他消费者或者消息不支持重复处理,消息将会丢失。解决方案是确保消费者处理消息后发送确认信息(ACK)给 RabbitMQ。
- 消费者异常退出:如果消费者在处理消息期间发生异常退出,消息可能会丢失。解决方案是使用消费者的 ACK 机制,并在消费者异常退出时进行消息重新投递。
4 网络故障导致消息丢失
- RabbitMQ 节点故障:如果 RabbitMQ 节点发生故障,在消息传递过程中可能会丢失消息。解决方案是使用 RabbitMQ 的镜像队列和集群模式以提高可靠性,并配置合理的策略来处理节点故障。
- 网络断连:如果生产者和 RabbitMQ 之间的网络断连,或者消费者和 RabbitMQ 之间的网络断连,消息可能会丢失。解决方案是使用可靠的网络连接,并确保消息传递的可靠性。
9 RabbitMQ的消息重复消费问题
1 消费者确认机制
在消费者处理完消息后,发送确认(ACK)给 RabbitMQ。RabbitMQ 收到确认后会将消息标记为已消费,并从队列中删除。只有在消费者发送确认后,RabbitMQ 才认为消息已被成功处理,否则会将消息重新投递给其他消费者。这样即使消费者异常退出,消息也不会被重复消费。
2 🌟幂等性处理
确保消费者的处理逻辑具有幂等性。即使同一条消息被多次消费,最终的结果也是一致的。通过设计合理的业务逻辑和数据存储机制,可以保证即使消息被重复消费,也不会产生错误结果。
🌟消息去重表: 维护一个消息去重表,记录已经处理过的消息的唯一标识符。在处理新消息之前,先查询这个表,如果已经存在,则可以判定为重复消息
**实现步骤:**在发送消息时候,指定一个业务上唯一的字段。如 xzll:order:10001 (10001代表订单id)
接收到消息后,select count(0) from 防重表 where biz_unique_id=message.getBizUniqueId();
如果大于0,那么说明以及消费过,将直接ack,告知mq删除该消息。如果=0说明没消费过。进行正常的业务逻辑
3 消息过期时间
在发布消息时设置消息的过期时间(TTL)。消息在达到过期时间后会被自动删除,即使消费者没有处理该消息,也不会再次被投递。这样可以确保消息不会一直保留在队列中,避免重复消费。
10 RabbitMQ保证消息消费顺序问题
- 单一队列顺序消费:将所有需要按顺序处理的消息发送到同一个队列。消费者从队列中取出消息进行处理。由于 RabbitMQ 默认会以循环方式将消息发送给消费者,所以同一个队列上的消息会按照顺序被不同的消费者处理。
- 消费者数量限制:为了保证顺序消费,可以限制只有一个消费者同时消费队列中的消息。这样,消息就会按照发送的顺序一个一个地被消费者处理。
- 消息加锁:可以使用互斥机制来确保同一时刻只有一个消费者处理队列中的消息。例如,在消费者处理消息前对消息进行加锁,并在处理完成后释放锁。这样可以避免多个消费者同时处理同一条消息。
- 发布者确认模式:在生产者方面,可以使用发布者确认模式(Publisher Confirms)来确保消息的有序性。生产者发送消息后,等待 RabbitMQ 的确认回执。只有在收到上一条消息的确认回执后,才发送下一条消息。这样可以保证消息被正确接收和处理。
11 🌟RabbitMQ的消息积压问题
1 消费者速度慢
场景:当消费者处理消息的速度比消息生产速度慢时,消息将在队列中积压。
应对策略:
- 增加消费者数量:增加消费者实例,以提高消息处理速度。
- 优化消费者代码:检查消费者代码是否有效率低下,如果有,则进行优化。
- 扩展硬件资源:增加消费者的计算和内存资源,以加速消息处理。
2 消息处理错误
场景:如果消息处理失败,例如由于程序错误或无法处理的数据,积压的消息可能不会被确认。
应对策略:
- 重试机制:实施消息重试机制,让失败的消息再次尝试被处理。
- 死信队列:将无法处理的消息路由到死信队列,以后分析和处理。
- 监控和日志:实施监控和日志记录,以便追踪错误并快速修复。
3 队列资源限制
场景:队列可能受到资源限制,如内存或磁盘空间限制,导致消息积压。
应对策略:
- 队列优化:调整队列的参数,如内存和磁盘限制,以适应更多消息。
- 消息持久性:考虑将消息设置为持久性,以确保它们在RabbitMQ重新启动后不会丢失。
- 数据清理:定期清理不再需要的消息,以释放资源。
4 长期积压
场景:长期积压可能是由于系统稳定性问题或无法预测的高负载情况引起的。
应对策略:
- 水平扩展:增加RabbitMQ集群中的节点,以增加吞吐量和可靠性。
- 流量控制:实施流量控制策略,限制消息的生产速度,以防止队列积压。
- 故障处理:建立故障处理流程,以快速应对系统稳定性问题。
5 预警和监控
场景:实时监控消息队列的积压情况是关键的。
应对策略:
- 报警系统:设置报警系统,以便在积压达到警戒值时收到通知。
- 性能指标监控:监控RabbitMQ性能指标,如队列深度、消费者速度和消息确认时间。
- 日志和分析:定期分析消息队列的日志,以了解积压情况的原因。