文章目录
1、MQ概述
1、什么是MQ?
- MQ:消息队列(
MessageQueue
),下面学习的RabbitMQ是一种典型的MQ产品。 - MQ的概念可以分为两部分来理解,首先是,Message(消息):在不同应用程序之间传递的数据。接着是,Queue(队列):是一种FIFO先进先出的数据结构。
- 将消息以队列的形式存储起来,并在不同的应用程序之间进行传递,这就是MssageQueue。
- RabbitMQ的更多信息请去官网获取。
2、MQ的作用
- MQ产品最直接的作用就是,将同步的事件,驱动为 异步的消息驱动。
- 在SpringBoot内部就集成了这种消息驱动的机制,但是,生产者和消费者都只能在一个进程中使用,如果需要垮行调用,这就需要一个独立的中间服务来发布和接收这些消息,这个中间服务就是MQ中间件。
- 比如:在一个大型电商项目中,订单服务完成下单,就可以发布下单事件,而下游的消费者,就可以消费这个下单事件,进行一些补充的业务。
- 在这个业务过程中,MQ中间件会起到以下作用:
- 解耦:生产者和消费者都只跟中间件进行交互,而不需要直接进行交互。这意味着,在生产者发送消息时,不需要考虑是否存在消费者或者存在多个消费者,反之亦然。 甚至,即便生产者和消费者是不同语言开发的,两者只要能够与中间件MQ进行正常的交互,那么它们之间就可以通过MQ进行消息的传递。(减少服务与服务之间的影响,提高系统整体的稳定性以及可拓展性)
- 异步:消息并不是从生产者发出后就立即交给消费者进行处理,而是在MQ中间件暂存下来。等到消费者启动后,再自行去MQ上处理。这就避免了生产者发送消息与消费者处理消息的时间冲突。(提高系统的响应速度、吞吐量)
- 削峰:有了MQ做消息暂存,那么当生产者发送消息的速度与消费者处理消息的速度不一致时,MQ就能起到削峰填谷的作用。(以稳定的系统资源来应对突发的流量冲击)
- MQ的缺点:
- 系统可用性降低
- 系统引入的外部依赖增多,系统的稳定性就会变差。一旦MQ宕机,对业务会产生影响,这就需要考虑如何保证MQ的高可用性。
- 系统复杂度提高
- 引入MQ后系统的复杂度会大大提高。以前服务之间可以进行同步的服务调用,引入MQ之后就会变成异步调用,数据的链路就会变得更复杂,并且还会带来一些其他问题,例如:如何保证消息不会被丢失、不会被重复消费等等。
- 消息一致性问题
- A系统处理完业务,通过MQ发送给B和C系统进行后续的业务处理。如果B系统处理完成,C系统处理失败怎么办?这就需要考虑如何保证消息数据处理的一致性。
- 系统可用性降低
3、主流MQ产品的比较
- 在MQ长期发展过程中,诞生了很多MQ产品,但是有很多MQ产品都已经逐渐被淘汰了。比如早期的 ZeroMQ、ActiveMQ等。目前最常用的MQ产品包括:kafka、RabbitMQ、RocketMQ,下面对这三个产品做个简单的比较以及理解他们的适用场景:
2、RabbitMQ快速上手
2.1 RabbitMQ产品介绍
- RabbitMQ的历史可以追随到2005年,他是一个非常老牌的MQ产品,使用非常广泛。同时期的很多MQ产品都已经逐渐被业界淘汰了,比如2003年诞生的ActiveMQ,2012年诞生的ZeroMQ,但是RabbitMQ却依然稳稳占据一席之地,足可见他的经典。官网地址 。
2.2 安装RabbitMQ
2.2.1 前置环境
- RabbitMQ是基于Erlang语言开发的,所以,按照RabbitMQ之前需要安装Erlang语言的环境。需要注意的是:RabbitMQ与Erlang语言之间是有版本对应关系的。目前,3.13版本的RabbitMQ需要Erlang语言版本在26.0到26.x之间。
- 需要先从官网下载操作系统对应的RabbitMQ安装包以及Erlang语言的安装包。
2.2.2 安装RabbitMQ服务
-
RabbitMQ服务有多种安装方式。在学习阶段,建议使用CentOS手动进行安装,这样更能接触产品的细节。之后使用其他操作系统或者使用Docker等技术安装时,才会更顺利。
-
需要注意的是,当前版本(3.13)的RabbitMQ建议CentOS版本最好升级到CentOS9版本。至少不能低于CentOS8。
-
Erlang语言包的安装,建议使用RabbitMQ提供的zero dependency版本,可点击此处下载对应版本的安装包。
[root@localhost mq]# rpm -ivh erlang-25.1-1.el8.x86_64.rpm 警告:erlang-25.1-1.el8.x86_64.rpm: 头V4 RSA/SHA256 Signature, 密钥 ID cc4bbe5b: NOKEY Verifying... ################################# [100%] 准备中... ################################# [100%] 正在升级/安装... 1:erlang-25.1-1.el8 ################################# [100%] [root@localhost mq]# erl -version Erlang (SMP,ASYNC_THREADS) (BEAM) emulator version 13.1
-
接下来是安装RabbitMQ,这里采用RPM安装包的方式,可点击此处下载对应版本的安装包。这里我们下载的是无依赖版本:rabbitmq-server-3.12.12-1.el8.noarch.rpm
[root@localhost mq]# rpm -ivh rabbitmq-server-3.12.12-1.el8.noarch.rpm 警告:rabbitmq-server-3.12.12-1.el8.noarch.rpm: 头V4 RSA/SHA512 Signature, 密钥 ID 6026dfca: NOKEY Verifying... ################################# [100%] 准备中... ################################# [100%] 正在升级/安装... 1:rabbitmq-server-3.12.12-1.el8 ################################# [100%] [/usr/lib/tmpfiles.d/rabbitmq-server.conf:1] Line references path below legacy directory /var/run/, updating /var/run/rabbitmq → /run/rabbitmq; please update the tmpfiles.d/ drop-in file accordingly.
-
安装完成之后,可以使用几个常用指令来维护RabbitMQ的服务状态。
--(1)启动Rabbitmq服务。启动应用之前要先启动服务。 service rabbitmq-server start --(2)重启RabbitMQ服务 systemctl restart rabbitmq-server --(3)后台启动RabbitMQ应用 rabbitmq-server -deched --(4)启动Rabbitmq客户端 rabbitmqctl start_app --(5)关闭Rabbitmq rabbitmqctl stop --(6)查看RabbitMQ的服务状态,如果出现 Status为 Runtime 表示启动成功 rabbitmqctl status 或 systemctl status rabbitmq-server
-
默认情况下,RabbitMQ只是一个后台服务,不便于管理。而RabbitMQ提供了管理插件,可以使用图形化的方式管理RabbitMQ。
-- 安装管理插件 [root@localhost mq]# rabbitmq-plugins enable rabbitmq_management Enabling plugins on node rabbit@xq: rabbitmq_management The following plugins have been configured: rabbitmq_management rabbitmq_management_agent rabbitmq_web_dispatch Applying plugin configuration to rabbit@xq... The following plugins have been enabled: rabbitmq_management rabbitmq_management_agent rabbitmq_web_dispatch started 3 plugins. -- 重启服务后生效 [root@localhost mq]# service rabbitmq-server start Redirecting to /bin/systemctl start rabbitmq-server.service [root@localhost mq]# rabbitmq start_app -- 查看防火墙状态、关闭防火墙、防止防火墙开机自启 service firewalld status service firewalld stop systemctl disable firewalld
-
插件激活后,就可以访问RabbitMQ的Web界面的控制台了,默认端口:15672
-
RabbitMQ提供了默认的用户名guest,密码guest。但是默认情况下,只允许本地登录,远程访问是无法登录的。这时,通常都会创建一个管理员账号单独对RabbitMQ进行管理。
[root@localhost mq]# rabbitmqctl add_user admin admin Adding user "admin" ... Done. Don't forget to grant the user permissions to some virtual hosts! See 'rabbitmqctl help set_permissions' to learn more. [root@localhost mq]# rabbitmqctl set_permissions -p / admin "." "." ".*" Setting permissions for user "admin" in vhost "/" ... [root@localhost mq]# rabbitmqctl set_user_tags admin administrator Setting tags for user "admin" to [administrator] ...
-
这样,就可以使用 admin / admin 用户登录Web控制台了。
2.3 RabbitMQ的基础使用
- 登录控制台后,首页上方就能看到RabbitMQ的主要功能模块。其中
Overview
是概述,主要展示RabbitMQ服务的一些整体运行情况。接着Connections
、Channels
、Exchanges
、Queues
就是RabbitMQ的核心功能。最后,Admin
则是一些管理功能。 - 例如:之前创建的 admin 的用户信息,在Admin的管理页面就能够看到:
- 例如:在Admin的管理页面可以创建一个虚拟机,并配置admin用户拥有访问权限
- 在RabbitMQ中,不同虚拟机之间的资源是完全隔离的。在资源充足的情况下,每个虚拟机可以当成一个独立的RabbitMQ服务来使用。
2.3.1 理解Queue
-
Exchange和Queue是RabbitMQ中用来传递消息的核心组件,下面进行简单体验一下:
- 1、在Queues模块下,创建一个名称为testQueue1的经典队列
- 2、创建完成之后,选择这个队列,就可以在页面上直接发送消息以及收到消息了。
- 1、在Queues模块下,创建一个名称为testQueue1的经典队列
-
在RabbitMQ中的消息都是通过Queue队列传递的,这个Queue其实就是一个典型的FIFO的队列数据结构。我们当前的演示是通过控制台页面来通过Queue进行收发消息。未来,我们编写客户端时,就是绑定对应的Queue进行消息收发。
2.3.2 理解Exchange
- 队列Queue可以发送消息,也可以收消息。那旁边的Exchange交换机的作用是啥?其实,它是用来辅助发送消息的。Exchange与Queue之间会建立一种绑定关系。通过不同的绑定,Exchange交换机中发送的消息就可以分发到不同的Queue上。
- 进到Exchange菜单,可以看到针对每个虚拟机,RabbitMQ都预先创建了多个Exchange交换机。
- 这里我们选择
/master
虚拟机上的amq.direct
交换机,进入到交换机详情页,选择Bingings,并将 testQueue1 队列绑定到这个交换机上。
- 绑定完成之后,可以在Exchange详情页以及Queue详情页看到绑定的结果。
- 接下来就可以在Exchange的详情页里发送消息。然后在testQueue1 这个队列中就能消费到这条消息。
- Exchange交换机并不实际存储消息,只是将发送到Exchange的消息转发到绑定的队列上。在具体使用时,通常只有消息生产者需要与Exchange打交道。而消费者,则并不需要与Exchange打交道,只要从Queue中消费消息就可以了。
- 另外,Exchange既然可以绑定一个队列,当然也可以绑定多个队列。在实际使用中,Exchange与Queue之间可以建立不同类型的绑定关系,然后通过一些不同的策略,选择将消息转发到哪些Queue上。这时候,Messaage上几个没有用上的参数,像 Routing Key ,Headers,Properties这些参数就能派上用场了。
- 在这个过程中,我们都是通过页面操作完成的消息发送与接收。在实际应用时,其实就是通过RabbitMQ提供的客户端API来完成这些功能。但是整个执行的过程,其实跟页面操作是相同的。
2.3.3 理解Connection和Channel
- 这两个功能实际上是跟客户端应用相关联的。一个Connection可以理解为一个客户端应用,而一个应用可以创建多个Channel,用来与RabbitMQ进行交互。
- 下面简单搭建一个客户端应用来体验一下:
- 1、创建一个Maven项目,在pom.xml文件中引入RabbitMQ客户端的依赖:
<dependency> <groupId>com.rabbitmq</groupId> <artifactId>amqp-client</artifactId> <version>5.21.0</version> </dependency>
- 2、然后创建一个消费者实例,尝试从RabbitMQ上的testQueue1 这个队列上拉取消息。
public class FirstConsumer { private static final String HOST_NAME = "192.168.121.134"; private static final Integer HOST_PORT = 5672; private static final String USER_NAME = "admin"; private static final String PASSWORD = "admin"; private static final String QUEUE_NAME = "testQueue1"; private static final String VIRTUAL_HOST = "/master"; public static void main(String[] args) throws Exception{ ConnectionFactory factory = new ConnectionFactory(); factory.setHost(HOST_NAME); factory.setPort(HOST_PORT); factory.setUsername(USER_NAME); factory.setPassword(PASSWORD); factory.setVirtualHost(VIRTUAL_HOST); //建立连接 Connection connection = factory.newConnection(); //创建信道 Channel channel = connection.createChannel(); //声明队列(参数值:队列名,durable是否持久化;exclusive:是否独占;autoDelete:是否自动删除;arguments: 其他参数) //如果Broker上没有队列,则自动创建队列,但是,如果已经有这个队列了,那么队列的属性必须匹配,否则会报错 channel.queueDeclare(QUEUE_NAME, true, false, false, null); //设置每个worker同时最多处理的消息个数 channel.basicQos(1); //回调函数,用于定义处理消息的业务逻辑 Consumer consumer = new DefaultConsumer(channel) { @Override public void handleDelivery(String consumerTag, Envelope envelope, AMQP.BasicProperties properties, byte[] body) throws IOException { System.out.println("========消息处理中======"); String routingKey = envelope.getRoutingKey(); System.out.println("routingKey:=>" + routingKey); String contentType = properties.getContentType(); System.out.println("contentType:=>" + contentType); long deliveryTag = envelope.getDeliveryTag(); System.out.println("deliveryTag:=>" + deliveryTag); String message = new String(body, StandardCharsets.UTF_8); System.out.println("content:=>" + message); } }; //从队列接收消息并处理 channel.basicConsume(QUEUE_NAME,true,consumer); } }
- 运行这个程序后,这时,我们可以去管理平台的页面上,往 testQueue1 队列发送一条消息,就能看到客户端会及时的处理这条消息。
- 1、创建一个Maven项目,在pom.xml文件中引入RabbitMQ客户端的依赖:
- 最后,可以在管理平台的Connection和Channels中看到这个消费者程序与RabbitMQ建立的连接和通道。
- 这里可以看到Connection就是与客户端的一个连接。只要连接还通着,他的状态就是running。而Channel是RabbitMQ与客户端进行数据交互的一个通道,没有数据交互时,状态就是idle闲置。有数据交互时,就会变成running。
3、RabbitMQ的核心概念总结
- 通过这些操作,我们可以了解到RabbitMQ的消息流转模型。
- 这里有很多重要的概念:
- <1> Queue 队列:这是RabbitMQ中最核心的概念。它是实际保存数据的最小单元。Queue结构天生就具有FIFO的顺序。消息最终要被发送到Queue当中,然后才能被消费者进行消费处理。
- <2> Exchange 交换机:这是RabbitMQ中进行数据路由的重要组件。Exchange并不实际保存消息,而是与Queue之间建立绑定关系,然后,如果有消息发送到了Exchange,Exchange就会将消息转发到Queue对列中,从而被对应的消费者消费处理。
- <3> virtual host 虚拟机:RabbitMQ出于服务器复用的想法,可以在一个RabbitMQ集群中划分出多个虚拟主机,每一个虚拟主机都有全套的基础服务组件,可以针对每个虚拟主机进行权限以及数据分配。不同虚拟主机之间是完全隔离的,如果不考虑资源分配的情况,一个虚拟主机就可以当成一个独立的RabbitMQ服务使用。
同时,也意味着不同虚拟主机之间是无法进行通信的,尽管他们是部署在同一个RabbitMQ服务上。例如,你无法通过虚拟机A的Exchange交换机将消息转发到虚拟机B的Queue上。 - <4> Connection 连接:客户端与RabbitMQ进行交互,首先就需要建立一个TPC连接,这个连接就是Connection。既然是通道,那就需要尽量注意在停止使用时要关闭,释放资源。
- <5> Channel 信道:一旦客户端与RabbitMQ建立了连接,就会分配一个AMQP信道 Channel。每个信道都会被分配一个唯一的ID。也可以理解为是客户端与RabbitMQ实际进行数据交互的通道,我们后续的大多数的数据操作都是在信道 Channel 这个层面展开的。
RabbitMQ为了减少性能开销,也会在一个Connection中建立多个Channel,这样便于客户端进行多线程连接,这些连接会复用同一个Connection的TCP通道,所以在实际业务中,对于Connection和Channel的分配也需要根据实际情况进行考量。