RabbitMQ基础及核心概念
简介
RabbitMQ 是一个开源的 消息队列中间件,实现了 高级消息队列协议(AMQP),用于在分布式系统中实现异步提速、应用解耦和流量削峰。它由 Erlang 语言开发,支持集群部署和高可用性,适用于微服务架构、实时数据处理、分布式任务调度等场景。
核心概念
这里用快递公司的分拣中心来类比着RabbitMQ的核心概念,更容易理解。
Virtual Host
Virtual Host(虚拟主机)是 RabbitMQ 中实现资源(信息)逻辑隔离的核心单元,每个虚拟主机下都有着仅属于自己的交换机、队列等。
Virtual Host(虚拟机)就像是快递公司的不同区域分部,每个分部都有着自己独立的分拣中心(Exchange)、分拣规则(Binding key)、号门(Queue),与其他分部完全隔离,避免资源混淆。
虚拟主机的核心价值体现在:
- 立体化隔离
不同虚拟主机的交换机、队列等资源相互不可见,确保开发/测试/生产环境或跨团队项目互不干扰。 - 细粒度权限
支持为每个虚拟主机单独配置用户访问权限,如限制特定团队仅能管理自身业务域的资源。 - 弹性资源池
单台RabbitMQ服务器可承载数百个虚拟主机,物理资源共享但逻辑空间独立,实现资源利用率最大化。
Exchange
Exchange(交换机)是消息分发的核心组件,接收 Producer(生产者)发送的消息,并根据规则(Binding key)和路由(Routing key)将消息分发到目标 Queue(队列)。
交换机就像是分练中心,根据 包裹地址(Routing key) 和 号门与不同小区的映射关系(Binding key) 将不同小区的包裹放到不同的号门(Queue)。
WARNING
这样理解其实稍微是有点偏差的,因为同一消息也支持分发到多个队列,以上的比喻如果是仅作为理解直连交换机是没有问题的,刚学习之初,可以先暂时这样理解。
| 交换机类型 | 特点 | 作用 | 绑定键示例 | 匹配规则 |
|---|---|---|---|---|
| Direct | 精确匹配路由键与绑定键。 | 将消息路由到绑定键与路由键完全匹配的队列。 | user.create | 路由键必须与绑定键完全相同才进行路由。例如,路由键为 user.create 的消息会路由到绑定键也为 user.create 的队列。 |
| Fanout | 完全忽略路由键,将消息广播给所有绑定的队列。 | 实现广播模式,所有绑定的队列都会收到消息,不论其绑定键是什么。 | 不适用(忽略路由键) | 忽略路由键,消息发送到所有绑定的队列。 |
| Topic | 支持模式匹配,使用通配符 # 和 * 来匹配多个单词或单个单词。 | 根据模式匹配路由键与绑定键,允许更灵活的消息路由策略。 | logs.error.*, *.info | 使用 # 匹配一个或多个单词,* 匹配单个单词。例如,路由键 logs.error.404 可以匹配绑定键 logs.error.*。 |
| Headers | 完全忽略路由键,基于消息头属性进行匹配。 | 允许通过消息头中的键值对来决定消息路由,提供比路由键更复杂的匹配。 | 不适用(基于消息头) | 根据消息头中的特定键值对进行匹配,如 { "priority": "high" }。 |
Binding key
Binding key(绑定键)是交换机与队列之间的逻辑关联关系,定义路由规则,决定哪些消息会被路由到哪个队列。
绑定键可以理解为是一种映射关系,比如我的分练中心负责A,B,C三个小区的货物分拣。我可以有三个号门,当快递包裹来临时,可以根据包裹寄送的不同小区将包裹分拣到不同的号门。这样当不同小区的快递员来拉取货物时,就只需要到不同的号门拉取专属于自己负责的小区的包裹。
Routing key
Routing key(路由键)是一个字符串,由生产者在发送消息时指定,用于告诉交换机这条消息应该到哪里去。
路由键可以理解为快递包裹上的地址标签,通过配合绑定键,指导交换机如何分发消息。
每条消息都携带着 Routing key,但 Routing key 只在部分类型交换机(Direct、Topic)下起作用。
Queue
队列是消息的暂存容器,遵循先进先出(FIFO)原则,确保消息按顺序被消费者消费。队列仅受限于主机的内存和磁盘限制,实质上它是一个大的消息缓冲区。
像分练中心不同的号门,小区的快递都存储在这里,临时存放未被签收的包裹(消息),直到快递员(消费者)取走。假设号门中昨日的快递未被快递员取走,则将今日的快递放在昨日快递后,确保快递员先寄送更早送过来的快递。
Connection
Connection 代表了一个客户端到 RabbitMQ 服务器之间的 TCP 连接。它负责底层网络通信,包括心跳检测、协议协商等,确保客户端与 RabbitMQ 服务器之间的稳定通讯。
Channel
Channel 是在已有的 Connection 之上建立的轻量级连接,用于执行 AMQP 命令。可以将 Channel 视为 Connection 内的虚拟连接。
通过Channel,客户端可以发送和接收消息、声明队列或交换机、绑定队列到交换机等操作。每个Channel都是独立的,因此不同的Channel之间不会互相干扰。
功能理解
异步提速
与同步通信不同,RabbitMQ 允许生产者发送消息后立即继续执行后续代码,无需等待消费者处理完消息。这种非阻塞特性意味着应用程序可以更高效地使用资源,从而提高整体性能。
虽然在生产者中开启一个异步线程去调用消费者(或处理任务)是一种提速的方式,这种方法可以让当前线程不被阻塞,从而提高响应速度。然而,这种方式与利用 RabbitMQ 来实现异步提速存还是存在几个重要的不同点:
- 解耦性
- 异步线程调用虽然也可以实现非阻塞操作,但通常情况下生产者仍然需要知道消费者的某些信息,比如接口信息,这导致了某种程度上的紧耦合。
- 而通过消息队列将生产者和消费者解耦,生产者只需发送消息到队列,不必关心谁是消费者、消费者何时处理消息等细节。这种机制使得系统更加灵活,易于扩展。
- 可靠性
- 异步线程调用发送消息时,生产者端可能需要关注消息被处理的结果。如果消费者端出现异常情况(如消费者进程崩溃或发生异常),生产者端需要重新发送消息给消费者端用于消息处理,这主要依赖于应用程序自身的健壮性设计。
- 而如果生产者通过消息队列将消息发送到消息队列后,就不需要再关注后续的处理结果。消息队列可以简单的实现如下功能:如果消费者端没有成功消费某条消息,消息就会一直保存在消息队列,直到消费者给消息队列发送一个ACK,告知某条消息已被成功消费。
- 消息分发
如果某条消息需要多个消费者端同事消费,比如当前订单系统服务用户下商品订单成功后,既需要调用短信服务程序进行短信通知,也需要调用物流服务新增一条物流信息。- 如果是通过异步线程进行提速,可能需要创建两个线程分别调用短信服务和物流服务,而且如果每次新增一个下游服务,当前的订单服务都需要手动修改代码。
- 而如果通过消息队列进行异步发送,只需使用不同的交换机类型,配合指定的路由键和绑定键,即可轻松实现单条消息多处分发。
应用解耦
不同的应用程序之间可以通过消息队列进行通信,而无需直接调用对方的接口或方法。这样可以降低系统中各个应用程序之间的耦合度。
应用解耦的好处在于:
- 灵活性
生产者不会因为消费者的接口路径变化而导致消息发送失败。因为生产者将消息发送到消息队列后,是消费者主动连接到 MQ,从而从队列中获取消息,生产者并无需主动调用消费者端的接口。 - 故障隔离
如果消费者端出现故障,不会直接影响到生产者。因为生产者只是负责发送消息,而不直接依赖于消费者的即时响应。此外,RabbitMQ 本身提供了诸如消息持久化、确认机制等功能,确保即使在网络问题或系统崩溃的情况下,消息也不会丢失,进一步增强了系统的可靠性。
流量削峰
当系统的请求量突然增加(即流量高峰)时,不是直接将这些请求发送给后端服务(消费者)进行处理,而是首先将它们存储到 RabbitMQ 的消息队列中。这样做的好处是避免了后端服务因无法及时处理大量并发请求而导致的服务崩溃或性能下降。
生产者(如 Web 服务器)也可以继续接收用户请求并将这些请求作为消息发送到队列中,而不必等待立即处理的结果。这大大提高了系统的响应速度和用户体验。
消费者(如后台处理服务)可以从队列中按自己的节奏读取消息并进行处理。从而达到削峰的效果。
思考:如果生产者生产消息过快且持续时间很长,而消费者消费消息过慢甚至处于宕机或网络不通的情况下导致消息无法被处理,最终消息都会被积压在MQ,队列会被逐渐的填满么?MQ服务会被压垮么?如何解决?
解决方案:RabbitMQ高级特性
安装
Linux
以下示例中 Linux 环境的系统镜像为 CentOS Stream 9,Erlang 语言版本安装包为 27.3.2,RabbitMQ 版本为 4.1.0,建议保持一致。
- Erlang 语言包安装
这里采用 RabbitMQ 提供的 zero dependency 包进行安装。
zero dependency 剔除了运行 RabbitMQ 非必需的一些 Erlang 模块和依赖项。这意味着它只包含了确保 RabbitMQ 能够顺利运行所需的最小化组件,从而减少了不必要的依赖和潜在的冲突,使得安装过程更加简洁高效。对于需要在基于 RPM 的系统(如CentOS、Red Hat Enterprise Linux等)上部署 RabbitMQ 的用户来说,这是一个理想的解决方案,因为它简化了 Erlang 的安装过程,并确保了与 RabbitMQ 的最佳兼容性。- 下载 Erlang 语言包安装若GitHub访问过慢,可点击直接下载 Erlang 安装包
- 上传 Erlang 软件包
上传 Erlang 软件包至 Linux 服务器 /opt/software/rabbitmq 下,目录可自定义。 - 安装 Erlang 软件包shell
cd /opt/software/rabbitmq rpm -ivh erlang-27.3.2-1.el9.x86_64.rpm
- RabbitMQ 安装
这里采用 RPM 安装包进行安装- 下载 RabbitMQ 安装包 若GitHub访问过慢,可点击直接下载 RabbitMQ 安装包
- 上传 RabbitMQ 软件包
上传 RabbitMQ 软件包至 Linux 服务器 /opt/software/rabbitmq 下,目录可自定义。 - 安装 RabbitMQ 软件包:shell
cd /opt/software/rabbitmq rpm -ivh rabbitmq-server-4.1.0-1.el8.noarch.rpm
- 启动 RabbitMQ 服务shell
# 启动 rabbitmq 服务 systemctl start rabbitmq-server # 通过端口查看 5672 端口是否已成功被 rabbitmq 服务占用 lsof -i :5672 # 查看 rabbitmq 服务是否已启动 systemctl status rabbitmq-servershell# rabbitmq 服务开机自启动 systemctl enable rabbitmq-server
如果设置了开机自启动 
- 启用 Web 管理插件
默认情况下, RabbitMQ 只是⼀个后台服务,不便于管理。⽽ RabbitMQ 提供了管理插件,可以使⽤图形化的⽅式管理 RabbitMQ。shell# 开启 Web 管理插件 rabbitmq-plugins enable rabbitmq_management # 重启 RabbitMQ 服务生效 systemctl restart rabbitmq-server #
插件激活后,就可以访问RabbitMQ的Web控制台了。访问端⼝15672。 
TIP
注意如果开启了防火墙,确认开放了相应的端口号
shellfirewall-cmd --permanent --add-port = {5672,15672}/tcp firewall-cmd --reload - 配置用户与权限
RabbitMQ提供了默认的⽤户名 guest,密码 guest。但是默认情况下,只允许本地登录,远程访问是⽆法登录的。
shell# 创建用户 rabbitmqctl add_user admin 123456 # 设为管理员角色 rabbitmqctl set_user_tags admin administrator # 分配资源权限 rabbitmqctl set_permissions -p / admin ".*" ".*" ".*"
此时就可以⽤ admin / 123456 ⽤户登录 Web 控制台了。
