【消息队列】RabbitMQ基本概念
1.RabbitMQ的架构
Producer(生产者): 生产者是消息的发送方,负责将消息发布到RabbitMQ的交换器 (Exchange)。
Consumer(消费者): 消费者是消息的接收方,负责从队列中获取消息,并进行处理和消费。
Exchange(交换器): 交换器是消息的接收和路由中心,它接收来自生产者的消息,并将消息路由到一个或多个与之绑定的队列(Queue)中。
Queue(队列): 队列是消息的存储和消费地,它保存着未被消费的消息,等待消费者(Consumer) 从队列中获取并处理消息。
Binding(绑定): 绑定是交换器和队列之间的关联关系,它定义了交换器将消息路由到哪些队列中。
VHost(虚拟主机): 它类似于操作系统中的命名空间,用于将RabbitMQ的资源进行隔离和分组。每个VHost拥有自己的交换器、队列、绑定和权限设置,不同VHost之间的资源相互独立,互不干扰。VHost可以用于将不同的应用或服务进行隔离,以防止彼此之间的消息冲突和资源竞争。
2.RabbitMQ的工作模式
- 简单模式(Simple Mode):在这种模式下,有一个生产者和一个消费者。生产者发送消息,消费者接收并处理这些消息。这种模式是最简单的RabbitMQ使用方式。
- Work模式(Work Queues):在这种模式下,有一个生产者和多个消费者。生产者发送消息到队列中,消费者从队列中取出消息进行处理。RabbitMQ通过轮询的方式将消息平均发送给消费者,确保一条消息只被一个消费者接收和处理。
- 发布/订阅模式(Publish/Subscribe):在这种模式下,生产者发送消息到交换机,交换机将消息广播到所有与之绑定的队列中,然后消费者从队列中取出消息进行消费。这种模式允许消费者有选择性地接收消息。
- 路由模式(Routing):路由模式与发布/订阅模式类似,但是生产者发送消息时需要指定一个路由键(routing key),交换机根据路由键将消息发送到匹配的队列中。消费者需要将其队列绑定到交换机,并指定路由键以便接收消息。
- Topic模式:这种模式是路由模式的扩展,它使用更灵活的匹配规则。生产者发送消息时指定一个路由键,消费者可以将其队列绑定到交换机上,并指定一个模式(topic),该模式可以包含通配符,用于匹配路由键。这样,消费者可以接收符合特定模式的所有消息。
- RPC模式:支持生产者和消费者不在同一个系统中,即允许远程调用的情况。通常,消费者作为服务端,放置在远程的系统中,提供接口,生产者调用接口,并发送消息。
3.RabbitMQ的作用
- 应用解耦:在生产者和消费者之间建立了一个缓冲区,使得两者之间的处理速度可以异步进行,不需要严格匹配。这大大增加了系统的灵活性和可扩展性。
- 削峰填谷:在高并发场景下,大量请求可能瞬间涌入系统,导致系统压力过大。RabbitMQ可以作为一个缓冲存储,将一部分请求暂时存入队列中,起到“削峰”的作用,保证系统平稳运行,在高峰期过去后,继续从队列中取出消息进行处理,直到积压的消息被完全消费。
- 异步通信:RabbitMQ支持异步通信,生产者将消息发送到队列后,不必等待消费者处理完消息再返回结果,而是可以继续执行其他任务。这大大提高了系统的并发处理能力和响应速度。
- 流量整形:RabbitMQ可以对消息进行优先级排序、延迟处理等,使得消息的处理更加有序和高效。
4.如何处理RabbitMQ的消息积压问题?
- 增加消费者数量:当消息队列中的消息数量超出当前消费者的处理能力时,可以动态地增加消费者的数量。这样,更多的消费者可以并行地处理消息,从而加快消息的消费速度。
- 提高消费者的处理能力:优化消费者的代码逻辑,提升消费者的性能,例如通过多线程或其他并行处理技术,可以提高单个消费者的处理速度。
- 设置消息的过期时间:为了避免消息无限期地积压在队列中,可以为消息设置一个合理的过期时间。当消息在队列中等待时间过长且未被消费时,RabbitMQ可以自动将其丢弃或进行其他处理。
5.RabbitMQ的死信队列
死信队列介绍
RabbitMQ的死信队列(Dead Letter Queue,简称DLQ)是一种用于处理消息处理失败或无法路由的消息的机制。它允许将无法被正常消费的消息重新路由到另一个队列,以便稍后进行进一步的处理、分析或排查问题。
当消息队列里面的消息出现以下几种情况时,就可能会被称为”死信”:
- 消息处理失败: 当消费者由于代码错误、消息格式不正确、业务规则冲突等原因无法成功处理一条消息时,这条消息可以被标记为死信。
- 消息过期: 在RabbitMQ中,消息可以设置过期时间。如果消息在规定的时间内没有被消费,它可以被认为是死信并被发送到死信队列。
- 消息被拒绝: 当消费者明确拒绝一条消息时,它可以被标记为死信并发送到死信队列。拒绝消息的原因可能是消息无法处理,或者消费者认为消息不符合处理条件。
- 消息无法路由: 当消息不能被路由到任何队列时,例如,没有匹配的绑定关系或路由键时,消息可以被发送到死信队列。
当消息变成”死信”之后,如果配置了死信队列,它将被发送到死信交换机,死信交换机将死信投递到一个队列上,这个队列就是死信队列。但是如果没有配置死信队列,那么这个消息将被丢弃。
配置死信队列
在RabbitMQ中,死信队列通常与交换机(Exchange) 和队列(Queue) 之间的绑定关系一起使用。要设置死信队列,通常需要以下步骤:
- 创建死信队列: 定义一个用于存储死信消息的队列。
- 创建死信交换机: 为死信队列定义一个交换机,通常是一个direct类型的交换机。
- 将队列与死信交换机绑定: 将主要队列和死信交换机绑定,以便无法处理的消息能够被转发到死信队列。
- 在主要队列上设置死信属性: 通过设置队列的**
x-dead-letter-exchange
和x-dead-letter-routing-key
**属性,来指定死信消息应该被发送到哪个交换机和路由键。
当消息被标记为死信时,它将被发送到死信队列,并可以由应用程序进一步处理、审查或记录。这种机制有助于增加消息处理的可靠性和容错性,确保不丢失重要的消息,并提供了一种处理失败消息的方式。
6.RabbitMQ如何实现延迟队列
死信队列
当RabbitMQ中的一条正常的消息,因为过了存活时间(TTL过期)、队列长度超限、被消费者拒绝等原因无法被
消费时,就会变成Dead Message,即死信。
实现方法
当一个消息变成死信之后,他就能被重新发送到死信队列中(其实是交换机-exchange)。基于这样的机制,就可以实现延迟消息了。那就是我们给一个消息设定TTL,但是并不消费这个消息,等他过期,过期后就会进入到死信队列,然后我们再监听死信队列的消息消费就行了。
而且,RabbitMQ中的这个TTL是可以设置任意时长的,这相比于RocketMQ只支持一些固定的时长而显得更加灵活一些。
死信队列实现延迟队列的缺点
但是,死信队列的实现方式存在一个问题,那就是可能造成队头阻塞,因为队列是先进先出的,而目每次只会判断队头的消息是否过期,那么,如果队头的消息时间很长,一直都不过期,那么就会阻塞整个队列,这时候即使排在他后面的消息过期了,那么也会被一直阻塞。
基于RabbitMQ的死信队列,可以实现延迟消息,非常灵活的实现定时关单,并且借助RabbitMQ的集群扩展性可以实现高可用,以及处理大并发量。他的缺点一是可能存在消息阻塞的问题;二是方案比较复杂,不仅要依赖RabbitMQ,而且还需要声明很多队列出来,增加系统的复杂度。
RabbitMQ插件
实现方法
基于插件的方式,消息并不会立即进入队列,而是先把他们保存在一个基于Erlang开发的Mnesia数据库中,然后通过一个定时器去查询需要被投递的消息,再把他们投递到x-delayed-message交换机中。
基于RabbitMQ插件的方式可以实现延迟消息,并且不存在消息阳塞的问题,但是因为是基于插件的,而这个插件
支持的最大延长时间是(232)-1 毫秒,大约49天,超过这个时就会被立即消费。
RabbitMQ插件实现延迟队列的缺点
不过这个方案也有一定的限制,它将延迟消息存在于 Mnesia 表中,并且在当前节点上具有单个磁盘副本,存在丢失的可能。
目前该插件的当前设计并不真正适合包合大量延迟消息(例如数十万或数百万)的场景,另外该插件的一个可变性来源是依赖于 Erlang 计时器,在系统中使用了一定数量的长时间计时器之后,它们开始争用调度程序资源,并且时间漂移不断累积。
7.保证RabbitMQ的消息可靠性
为了确保消息不丢失,可以采取以下措施:
- 持久化消息: 将消息持久化到磁盘中,这样即使系统崩溃,消息也不会丢失。常见的MQ如RabbitMQ、Kafka、ActiveMQ都支持消息持久化。
- 确认机制(Acknowledgment): 发送方和接收方都需要确认消息的接收。发送方在发送消息后等待接收方的确认,接收方处理消息后需要确认已成功处理。
- 重复发送: 如果在设定时间内没有收到确认,发送方可以重试发送消息。这需要消息处理具备幂等性,即多次处理同一条消息不会造成副作用。
- 死信队列(Dead Letter Queue, DLQ): 当消息无法被成功处理或无法被确认时,将其转移到死信队列中进行后续处理。
- 高可用集群: 通过MQ集群来实现高可用性,防止单点故障导致消息丢失。
- 点赞
- 收藏
- 关注作者
评论(0)