RabbitMQ快速实战以及集群架构详解

一、 MQ 介绍
1 、什么是 MQ ?为什么要用 MQ
ChatGPT 中对于消息队列的介绍是这样的:

MQ : MessageQueue ,消息队列。这东西分两个部分来理解:队列,是一种 FIFO 先进先出的数据结构。
消息:在不同应用程序之间传递的数据。 将消息以队列的形式存储起来,并且在不同的应用程序之间进行传 递,这就成了 MessageQueue 。而 MQ 的作用,从刚才 ChatGPT 的介绍中就能够抽象出三个关键字:异步、解耦、削峰。但是这什么意思呢?跟开发有什么关系?我们从一个简单的SpringBoot 应用开始说起。
首先搭建一个普通的 Maven 项目,在 pom.xml 中引入 SpringBoot 的依赖:


然后,添加一个监听器类

接下来,添加一个 SpringBoot 启动类。在启动类中加入自己的这个监听器。

好了。不用添加配置文件,直接启动就行。 然后可以看到这样的结果:

从这个例子中可以看到,在 run 方法中,我们使用 applicationContext 发布了一个 myEvent 事件,然后通过自定义的监听器MyApplicationListener ,就监听了到这个 myEvent 事件。这个过程中, applicationContext担任了发布消息的功能,称为消息生产者Producer ,而 MyApplicationListener 担任了消费处理这个消息的功能,称为消息消费者Consumer 。 Producer 和 Consumer 他们的运行状况互不干涉,没有 Consumer ,Producer一样正常运行,反过来也一样。也就是说,推送 Producer 和 Consumer 正常工作的,只有发布的这些事件。这种方式就称为事件驱动。
从这个简单的例子中你可以看到, SpringBoot 内部就是集成了事件驱动机制的。 SpringBoot 会将自己应用过程中发生的每一个重要的运行步骤都通过事件发送出来。你会发现这些事件对于监控一个SpringBoot 应用挺有用的。如果我想要另外搭建一个监控系统,也像MyApplicationListener 一样监听 SpringBoot 的这些事件,应该要怎么做呢?
直接监听肯定是不行的,因为 SpringBoot 中的这些事件只在应用内部有效。因此,需要独立出一个中间服务,这样才可以去统一接收SpringBoot 应用的这些事件。

这时这个神秘的中间服务要保证这些系统可以正常工作,应该要有哪些特性呢? 1 、 SpringBoot 应用和监控服务应该是 解耦 的。不管有没有监控服务, SpringBoot 应用都要是可以正常运行
的。同时,不管监控服务是用什么语言开发的,同样不应该影响 SpringBoot 应用的正常运行。更进一步,不管监控服务要部署多少个,同样也不应该影响SpringBoot 应用的正常运行。反过来,从监控服务看SpringBoot应用也应该是一样的。这就需要这个中间服务可以提供不同语言的客户端,通过不同客户端让消息生产者和消息消费者之间彻底解耦。
2 、 SpringBoot 应用和监控服务之间处理消息应该是 异步 的。基于解耦的关系, SpringBoot 应用并不需要知道监控服务有没有运行。所以他并不需要将消息直接发送到监控服务,也不需要保证消息一定会被监控服务处理。他只要将消息发到中间服务就可以了。而监控服务可以在SpringBoot 应用发布了时间之后,随时去接收处理这些消息。
3 、这个中间服务需要可以协调双方的事件处理速度,产生 削峰填谷 的效果。监控服务一般都是希望每五分钟接收到SpringBoot 发布过来的消息,然后进行一次统计,但是 SpringBoot 应用发布的事件频率却是不确定的。如果SpringBoot 应用在五分钟内产生了海量的消息,就有可能让监控服务内存撑爆,处理不过来。而监控服务如果加大内存,SpringBoot 应用又有可能在五分钟内根本没有消息,监控服务的内存就白加了。监控服务的内存配大配小都不合适。这时候,就需要这个中间服务能够这些消息暂存起来,让监控服务可以按照自己的能力慢慢处理问题。这就是中间服务的削峰填谷的作用。
而MQ 也就是为了这样的场景创建的中间服务。 MQ 中间件在很多业务场景中都扮演着很重要的角色。例如下图是一个典型的秒杀场景业务图:

在后面的实战项目中,会带大家从头到尾搭建一个这样的秒杀系统。在这其中,对于后端最重要的优化就是使用MQ 。在典型的秒杀场景,瞬间产生的大量下单请求很容易让后端的下单服务崩溃。这时,就可以让下单系统将订单消息发送到MQ 中间暂存起来,而后端的额下单服务就可以从 MQ 中获取数据,按照自己的处理能力,慢慢进行下单。另外,下单是一个比较复杂的业务,需要通知支付系统、库存系统、物流系统、营销系统等大量的下游系统。下单系统光一个个通知这些系统,就会需要很长时间。这时,就可以将下单完成的消息发送到MQ ,然后下游的各种系统可以从 MQ 中获取下单完成的消息,进行异步处理。这样也能极大提高下单系统的性能。
2 MQ 的优缺点
这时候你可能在想, SpringBoot 已经提供了本地的事件驱动支持。 那么我是不是给 SpringBoot 应用加上一些web 接口,基于这些 web 接口不就可以将本地的这些系统事件以及自己产生的这些事件往外部应用推送,那这不就成了一个MQ 服务了吗?单其实上面列出了 MQ 的的很多优点。 但是在具体使用 MQ 时,也会带来很多的缺点:
·系统可用性降低
系统引入的外部依赖增多,系统的稳定性就会变差。一旦 MQ 宕机,对业务会产生影响。这就需要考虑如何保证MQ 的高可用。
·系统复杂度提高
引入 MQ 后系统的复杂度会大大提高。以前服务之间可以进行同步的服务调用,引入 MQ 后,会变为异步调用,数据的链路就会变得更复杂。并且还会带来其他一些问题。比如:消息如何高效存储、如何定期维护、如何监控、如何溯源等等。如何保证消费不会丢失?不会被重复调用?怎么保证消息的顺序性等问题。
·消息安全性问题
引入 MQ 后,消息需要在 MQ 中存储起来。这时就会带来很多网络造成的数据安全问题。比如如何快速保存海量消息?如何保证消息不丢失?不被重复处理?怎么保证消息的顺序性?如何保证消息事务完整等问题。
所以 MQ 的应用场景虽然比较简单,但是随着深度分析业务场景,也会随之产生非常多的问题。甚至为此指定的业务标准都出现过好几套,比如JMX , AMQP 等。因此,这才需要构建出 RabbitMQ 等这些中间件,来完整的处理MQ 遇到的这些问题。
3 、几大主流 MQ 产品特点比较
所以 MQ 通常用起来比较简单,但是实现上是非常复杂的。基本上 MQ 代表了业界高可用、高并发、高可扩展三高架构的所有设计精髓。在MQ 长期发展过程中,诞生了很多 MQ 产品,但是有很多 MQ 产品都已经逐渐被淘汰了。比如早期的ZeroMQ,ActiveMQ 等。目前最常用的 MQ 产品包括 Kafka 、 RabbitMQ 和 RocketMQ 。我们对这三个产品做下简单的比较,重点需要理解他们的适用场景。

4 、关于 RabbitMQ
RabbitMQ 的历史可以追溯到 2006 年,是一个非常老牌的 MQ 产品,使用非常广泛。同时期的很多 MQ 产品都已经逐渐被业界淘汰了,比如2003 年诞生的 ActiveMQ , 2012 年诞生的 ZeroMQ ,但是 RabbitMQ 却依然稳稳占据一席之地,足可见他的经典。官网地址 https://www.rabbitmq.com/

RabbitMQ 虽然是开源的,但是是基于 erlang 语言开发的。这个语言比较小众,用得不是很多。因此很少研究源码。
RabbitMQ 的应用相当广泛,拥有很多非常强大的特性:

二、 Rabbitmq 快速上手 1 、版本选择
RabbitMQ 版本,通常与他的大的功能是有关系的。 3.8.x 版本主要是围绕 Quorum Queue 功能,而 3.9.x 版本主要是围绕Streams 功能。后面的 3.10.x 版本和 3.11.x 版本,没有太多新功能,主要是对 Quorum Queue和Stream 功能做一些修复以及增强,另外,开始增加了一些功能插件,比如 OAuth2 , MQTT 。我们这次就选择目前最新的3.11.10 版本。
RabbitMQ 是基于 Erlang 语言开发,所以安装前需要安装 Erlang 语言环境。需要注意下的是 RabbitMQ 与ErLang是有版本对应关系的。 3.11.10 版本的 RabbitMQ 只支持 25.0 以上到 25.2 版本的 Erlang 。这里要注意一下,如果使用CentOS 搭建 RabbitMQ 服务, Erlang25.0~25.2 这几个版本建议的 CentOS 版本要升级到CentOS8或者 CentOS9 。
2 、安装 RabbitMQ 服务
接下来准备一台 CentOS9 服务器,快速搭建一个 RabbitMQ 服务。
RabbitMQ 服务安装的方式很多,现在企业级服务多偏向于使用 Docker 安装。 Duchub 上已经上传了当前版本的RabbitMQ 镜像。用以下 docker 指令可以安装。

但是这种方式相当于是官方将一个已经搭建好的环境给你用。这样不便于了解 RabbitMQ 的一些细节。因此我们这里会采用服务器直接安装的方式。
安装 RabbitMQ 之前需要先安装 Erlang 语言包
Linux 上的安装 Erlang 稍微有点复杂,需要有非常多的依赖包。简单起见,可以下载 rabbitmq 提供的 zero dependency版本。 下载地址 https://github.com/rabbitmq/erlang-rpm/releases
下载完成后,可以尝试使用下面的指令安装

这样 Erlang 语言包就安装完成了。 安装完后可以使用 erl -version 指令检测下 erlang 是否安装成功。

安装 RabbitMQ
RabbitMQ 的安装方式有很多,我们采用 RPM 安装包的方式。安装包可以到 github 仓库中下载发布包。下载地址: https://github.com/rabbitmq/rabbitmq-server/releases 。 这里我们下载无依赖版本:rabbitmq-server-3.11.10-1.el8.noarch.rpm

安装完成后,可以使用常见的指令维护 RabbitMQ 的服务。

3 、启用 RabbitMQ 管理插件
RabbitMQ 提供了管理插件,可以快速使用 RabbitMQ 。在使用之前,需要先打开他的 Web 管理插件。


插件激活后,就可以访问 RabbitMQ 的 Web 控制台了。访问端口 15672 。 RabbitMQ 提供的默认用户是guest,密码 guest 。
但是注意下,默认情况下,只允许在 localhost 本地登录,远程访问是无法登录的。

这时,通常都会创建一个管理员账号单独对 RabbitMQ 进行管理。

这样就可以用 admin/admin 用户登录 Web 控制台了。
登录控制台后上方就能看到 RabbitMQ 的主要功能。其中 Overview 是概述,主要展示 RabbitMQ 服务的一些整体运行情况。后面Conections 、 Channels 、 Exchanges 和 Queues 就是 RabbitMQ 的核心功能。最后的Admin则是一些管理功能。

接下来可以尝试创建一个 Veirtual Hosts 虚拟机。

这里就创建了一个名为 /mirror 的虚拟机,并配置了 admin 用户拥有访问的权限。在 RabbitMQ 中,不同虚拟机之间的资源是完全隔离的。不考虑资源分配的情况下,每个虚拟机就可以当成一个独立的RabbitMQ 服务来使用。
管理页面的其他功能就不详细介绍了,后续随着深入使用再详细介绍
4 、理解 Exchange Queue
Exchange 和 Queue 是 RabbitMQ 中用来传递消息的核心组件。我们可以简单体验一下。 1 、在 Queues 菜单,创建一个经典队列

创建完成后,选择这个 test1 队列,就可以在页面上直接发送消息以及消费消息了。

从这里可以看到, RabbitMQ 中的消息都是通过 Queue 队列传递的,这个 Queue 其实就是一个典型的 FIFO的队列数据结构。而Exchange 交换机则是用来辅助进行消息分发的。 Exchange 与 Queue 之间会建立一种绑定关系,通过绑定关系,Exchange 交换机里发送的消息就可以分发到不同的 Queue 上。

队列 Queue 即可以发消息,也可以收消息,那旁边的 Exchange 交换机是干什么的呢?其实他也是用来辅助发送消息的。
进入 Exchanges 菜单,可以看到针对每个虚拟机, RabbitMQ 都预先创建了多个 Exchange 交换机。

这里我们选择 amq.direct 交换机,进入交换机详情页,选择 Binding ,并将 test1 队列绑定到这个交换机上。

绑定完成后,可以在 Exchange 详情页以及 Queue 详情页都看到绑定的结果。

Exchange 交换机既然可以绑定一个队列,当然也可以绑定更多的队列。而 Exchange 的作用,就是将发送到Exchange 的消息转发到绑定的队列上。在具体使用时,通常只有消息生产者需要与 Exchange 打交道。而消费者,则并不需要与Exchange 打交道,只要从 Queue 中消费消息就可以了。
另外, Exchange 并不只是简单的将消息全部转发给 Queue ,在实际使用中, Exchange 与 Queue 之间可以建立不同类型的绑定关系,然后通过一些不同的策略,选择将消息转发到哪些Queue 上。这时候,Messaage上几个没有用上的参数,像 Routing Key ,Headers , Properties 这些参数就能派上用场了。
在这个过程中,我们都是通过页面操作完成的消息发送与接收。在实际应用时,其实就是通过 RabbitMQ提供的客户端API 来完成这些功能。但是整个执行的过程,其实跟页面操作是相同的。
渔与鱼 : 1 、页面上的这些操作,建议你一定要自己动手,到处尝试尝试。不要觉得能收发消息了,对RabbitMQ就掌握得差不多了。并且,多带上脑子来体验,收集你在使用当中的困惑,这样在后续学习过程中你会更有目标。比如Stream 类型的队列为什么不能在页面上获取消息?记住这个问题,后续在我给你分享队列类型时,你才有兴趣听得进去。
2 、之前说过 RabbitMQ 的这些操作几乎都有后台命令行工具与之相匹配。而后台的很多命令行也提供了丰富的帮助文档。借助页面控制台了解后台命令行的操作,也是一个不错的方式。比如我们之前使用rabbitmqctl adduser指令添加了 admin 用户。那添加虚拟机、添加队列、添加交换机这些也可以类似去了解。你只需要使用rabbitmqctl -help 就能看到大量的帮助文档。
5 、理解 Connection Channel
这两个概念实际上是跟客户端应用的对应关系。一个 Connection 可以理解为一个客户端应用。而一个应用可以创建多个Channel ,用来与 RabbitMQ 进行交互。
我们可以来搭建一个客户端应用了解一下。
1 、创建一个 Maven 项目,在 pom.xml 中引入 RabbitMQ 客户端的依赖:

2 、然后就可以创建一个消费者实例,尝试从 RabbitMQ 上的 test1 这个队列上拉取消息。


执行这个应用程序后,就会在 RabbitMQ 上新创建一个 test2 的队列 ( 如果你之前没有创建过的话 ) ,并且启动一个消费者,处理test2 队列上的消息。这时,我们可以从管理平台页面上往 test2 队列发送一条消息,这个消费者程序就会及时消费消息。

然后在管理平台的 Connections 和 Channels 里就能看到这个消费者程序与 RabbitMQ 建立的一个
Connection 连接与一个 Channel 通道。

这里可以看到 Connection 就是与客户端的一个连接。只要连接还通着,他的状态就是 running 。而
Channel 是 RabbitMQ 与客户端进行数据交互的一个通道,没有数据交互时,状态就是 idle 闲置。有数据交互时,就会变成running 。在他们后面,都会展示出数据交互的状态。
另外,从这个简单示例中可以看到, Channel 是从 Connection 中创建出来的,这也意味着,一个
Connection 中可以创建出多个 Channel 。从这些 Connection 和 Channel 中可以很方面的了解到 RabbitMQ 当前的服务运行状态。
6 RabbitMQ 中的核心概念总结
通过这些操作,我们就可以了解到 RabbitMQ 的消息流转模型。

这里包含了很多 RabbitMQ 的重要概念:
1 、服务主机 Broker
一个搭建 RabbitMQ Server 的服务器称为 Broker 。这个并不是 RabbitMQ 特有的概念,但是却是几乎所有MQ产品通用的一个概念。未来如果需要搭建集群,就需要通过这些 Broker 来构建。
2 、虚拟主机 virtual host
RabbitMQ 出于服务器复用的想法,可以在一个 RabbitMQ 集群中划分出多个虚拟主机,每一个虚拟主机都有全套的基础服务组件,可以针对每个虚拟主机进行权限以及数据分配。不同虚拟主机之间是完全隔离的,如果不考虑资源分配的情况,一个虚拟主机就可以当成一个独立的RabbitMQ 服务使用。
2 、连接 Connection
客户端与 RabbitMQ 进行交互,首先就需要建立一个 TPC 连接,这个连接就是 Connection 。既然是通道,那就需要尽量注意在停止使用时要关闭,释放资源。
3 、信道 Channel
一旦客户端与 RabbitMQ 建立了连接,就会分配一个 AMQP 信道 Channel 。每个信道都会被分配一个唯一的ID 。也可以理解为是客户端与 RabbitMQ 实际进行数据交互的通道,我们后续的大多数的数据操作都是在信道 Channel 这个层面展开的。
RabbitMQ 为了减少性能开销,也会在一个 Connection 中建立多个 Channel ,这样便于客户端进行多线程连接,这些连接会复用同一个Connection 的 TCP 通道,所以在实际业务中,对于 Connection 和 Channel 的分配也需要根据实际情况进行考量。
4 、交换机 Exchange
这是 RabbitMQ 中进行数据路由的重要组件。消息发送到 RabbitMQ 中后,会首先进入一个交换机,然后由交换机负责将数据转发到不同的队列中。RabbitMQ 中有多种不同类型的交换机来支持不同的路由策略。从Web管理界面就能看到,在每个虚拟主机中, RabbitMQ 都会默认创建几个不同类型的交换机来。

交换机多用来与生产者打交道。生产者发送的消息通过 Exchange 交换机分配到各个不同的 Queue 队列上,而对于消息消费者来说,通常只需要关注自己感兴趣的队列就可以了。
5 、队列 Queue
Queue 是实际保存数据的最小单位。 Queue 不需要 Exchange 也可以独立工作,只不过通常在业务场景中,会增加Exchange 实现更复杂的消息分配策略。 Queue 结构天生就具有 FIFO 的顺序,消息最终都会被分发到不同的Queue 当中,然后才被消费者进行消费处理。这也是最近 RabbitMQ 功能变动最大的地方。最为常用的是经典队列Classic 。 RabbitMQ 3.8.X 版本添加了 Quorum 队列, 3.9.X 又添加了 Stream 队列。从官网的封面就能看到,现在RabbitMQ 主推的是 Quorum 队列。
三、章节总结
这一章节最主要的,就是了解 RabbitMQ 的基础消息模型,这是 RabbitMQ 最重要最核心的部分。而后续就是针对这个大的模型,逐步深入,了解RabbitMQ 的更多功能细节。
如果你之前已经使用过 RabbitMQ ,那么前面的操作你都可以忘记,但是最后这个大模型,你一定要先构建起来,并且可以尝试根据你的经验,看看可以往这个模型中添加一些什么内容。例如消息如何传递、销毁。Exchange 与 Queue 之间如何进行消息路由等等。而如果你之前没有使用过 RabbitMQ ,那么前面的这些操作你最好自己动手都试试,并且在管理页面上到处多点点,多试试RabbitMQ 的功能。不要说课上只创建了一个Queue ,那你也就照着创建一个 Queue 就完了。以可视化的方式构建起最后的消息模型,保证对于这个模型当中的各个重要概念的理解是准确的。
在这个章节中,我们只演示了 Consumer 端的示例代码,并没有演示 Producer 端的示例代码。但是,如果你熟悉了整个消息模型,也可以猜想得到Producer 端的代码应该怎么写。无非也是这几个关键的步骤:
1 、创建 Connection
2 、创建 Channel
3 、声明 exchange( 如果需要的话 )
4 、声明 Queue
5 ,发送消息,可以发到 Exchange ,也可以发到 Queue 。
例如一个简单的发送者如下:


你看,整个流程跟消费者是不是差不多的?除了生产者发送完消息后需要主动关闭下连接,而消费者因为要持续消费消息所以不需要主动关闭连接,其他流程几乎完全一样的。

相关推荐
金刚猿16 分钟前
简单理解下基于 Redisson 库的分布式锁机制
分布式·分布式锁·redisson
我一直在流浪38 分钟前
Kafka - 消费者程序仅消费一半分区消息的问题
分布式·kafka
张彦峰ZYF2 小时前
投资策略规划最优决策分析
分布式·算法·金融
processflow流程图4 小时前
分布式kettle调度平台v6.4.0新功能介绍
分布式
全栈开发圈4 小时前
干货分享|分布式数据科学工具 Xorbits 的使用
分布式
天天扭码5 小时前
五天SpringCloud计划——DAY2之单体架构和微服务架构的选择和转换原则
java·spring cloud·微服务·架构
余生H6 小时前
transformer.js(三):底层架构及性能优化指南
javascript·深度学习·架构·transformer
凡人的AI工具箱6 小时前
15分钟学 Go 第 60 天 :综合项目展示 - 构建微服务电商平台(完整示例25000字)
开发语言·后端·微服务·架构·golang