MQ的优势
- 应用解耦
当不同模块之间的依赖关系复杂时,使用 RabbitMQ 可以解耦模块,使它们通过消息进行通信,互不影响。
- 异步提速
对于一些非关键路径的操作,如发送邮件、生成报表等,可以通过 RabbitMQ 异步处理,提高系统的响应速度。
- 削峰填谷
在高并发场景下,通过 RabbitMQ 缓存请求,避免后端服务瞬间被大量请求压垮。
MQ的劣势
- 系统可用性降低
系统引入的外部依赖越多,系统稳定性越差。一旦 MQ 宕机,就会对业务造成影响。如何保证MQ的高可用?
- 系统复杂度提高
MQ 的加入大大增加了系统的复杂度,以前系统间是同步的远程调用,现在是通过 MQ 进行异步调用。如何保证消息不被丢失等情况?
术语
- Virtual host(虚拟主机)
出于多租户和安全因素设计的,把AMQP的基本组件划分到一个虚拟的分组中,类似于网络中的namespace概念。当多个不同的用户使用同一个RabbitMQ server提供的服务时,可以划分出多个vhost,每个用户在自己的vhost创建exchange/queue 等。
- Exchange(交换机)
交换机是RabbitMQ中实现消息路由的核心组件。生产者将消息发送到交换机,交换机根据路由键和绑定规则将消息分发到不同的队列中。RabbitMQ提供了多种类型的交换机,包括直接交换机(Direct)、扇出交换机(Fanout)、主题交换机(Topic)和消息头交换机(Headers)等。
- Queue(队列)
队列是RabbitMQ中用于存储和转发消息的容器。生产者将消息发送到队列中,而消费者则从队列中取出并处理这些消息。队列具有先进先出(FIFO)的特性,即先进入队列的消息会先被消费者取出。队列还具有持久化的特性,可以将消息存储在磁盘上,以防止RabbitMQ服务器重启后消息的丢失。此外,队列还可以设置其他属性,如是否独占、是否自动删除等。
- Connection(连接)
连接RabbitMQ和应用服务器的TCP连接。
- Channel(信道)
连接里的一个虚拟通道,通过消息队列发送或者接收消息时,都是通过信道进行的。
- Producer(生产者)
生产者是消息的发送方,它将消息发送到RabbitMQ的交换机中。生产者可以指定消息的路由键和交换机的名称,以控制消息的路由和分发。
- Consumer(消费者)
消费者是消息的接收方,它从RabbitMQ的队列中获取并处理消息。消费者可以监听一个或多个队列,当队列中有新消息时,消费者会将其取出并处理。
- Broker(代理)
接收和分发消息的应用,RabbitMQ Server就是Message Broker。
交换机
- Default Exchange
默认交换机是一个预定义的无名交换机,它会自动将消息发送到与之路由键名称相同的队列中。当生产者没有显式地指定交换机时,消息会被发送到默认交换机中。
- Direct Exchange
直连交换机是最简单的交换机类型,它将消息的路由键和绑定键进行精确匹配,当我们的路由键和绑定键一致的时候,将消息发送到与之完全匹配的队列。
3. Fanout Exchange
扇形交换机将消息广播到所有与之绑定的队列。无论消息的路由键是什么,扇形交换机都会将消息发送到所有绑定的队列中。这种类型的交换机常用于实现发布-订阅模式,将消息广播给多个消费者。
4. Topic Exchange
主题交换机基于模式匹配的方式将消息路由到队列。它使用通配符来进行匹配,支持通配符符号 "" 和 "#"。其中 "" 表示匹配一个单词,"#" 表示匹配一个或多个单词。
5. Headers Exchange
头交换机和扇形交换机一样都不要路由键,首交换机根据消息Headers的属性进行匹配和路由。在消息发送时,可以指定一组键值对作为消息的头部属性,交换机会根据这些属性进行匹配。首部交换机提供了更灵活的匹配方式,但相对复杂度较高,通常使用较少。
使用交换机的好处
- 路由控制
通过交换机,可以根据消息的路由键将消息路由到与之匹配的队列。这样,可以根据消息的属性或标签来定向分发消息,实现精确的消息路由控制。
- 消息过滤
交换机可以根据消息的路由键、消息头部属性等信息对消息进行过滤和筛选,将符合特定条件的消息发送到相应的队列。这样可以实现消息的订阅和过滤机制,灵活地处理不同类型的消息。
- 广播和多播
通过使用扇形交换机(Fanout Exchange),可以将消息广播到所有与之绑定的队列,实现消息的广播和多播机制,方便实现发布-订阅模式。
- 解耦和灵活性
通过将消息发送到交换机而不是直接发送到队列,生产者和消费者之间实现了解耦。生产者只需要将消息发送到指定的交换机,而不需要知道具体的队列。这样,可以灵活地增加、删除或修改队列,而不会对生产者产生影响。
- 可扩展性
使用交换机可以实现消息的分发和负载均衡机制。通过将消息发送到多个队列,可以实现横向扩展和并发处理,提高系统的吞吐量和性能。
如何保证消息的可靠性
- RabbitMQ端的数据安全 Data Safety on the RabbitMQ Side
- 集群环境 Clustering and Queue Content Replication
- 生产端的数据安全 Data Safety on the Publisher Side
- 确保消息被路由到队列 Ensuring that Messages are Routed
持久化
- exchange要持久化
- queue要持久化
- message要持久化
生产方确认
- confirm确认模式
- 消息成功投递到交换机,返回ack
- 消息未投递到交换机,发回nack
- return退回模式
- 消息投递到交换机了,但是没有路由到队列。返回ACK,及路由失败原因
消费方确认
- 自动确认:acknowledge="none"(默认)
- 手动确认:acknowledge="manual"
- 根据异常情况确认:acknowledge="auto"
FAQ
- 既然已经有了Connection,完全可以使用Connection完成Channel的工作,为什么还要引入Channel这样一个虚拟连接的概念呢?
因为现在的程序都是支持多线程的,如果没有Channel,那么每个线程在访问RabbitMQ的时候都要建立一个Connection这样的TCP连接,对于操作系统来说, 建立和销毁TCP连接是非常大的开销,在系统访问流量高峰时,会严重影响系统的性能。 Channel就是为了解决这种问题,通常情况下,每个线程创建单独的Channel进行通讯,每个Channel都有自己的channel id帮助Broker和客户端识别Channel, 所以Channel之间是完全隔离的。 Connection与Channel之间的关系可以比作光纤电缆,如果把Connection比作一条光纤电缆,那么Channel就相当于是电缆中的一束光纤。