思维导图
一、什么是消息队列
消息
指的是两个应用间传递的数据。数据的类型有很多种形式,可能只包含文本字符串,也可能包含嵌入对象。
“消息队列(Message Queue)”是在消息的传输过程中保存消息的容器
。在消息队列中,通常有生产者和消费者两个角色。生产者只负责发送数据到消息队列,谁从消息队列中取出数据处理,他不管。消费者只负责从消息队列中取出数据处理,他不管这是谁发送的数据。
二、为什么使用消息队列
解耦
。如图所示。假设有系统B、C、D都需要系统A的数据,于是系统A调用三个方法发送数据到B、C、D。这时,系统D不需要了,那就需要在系统A把相关的代码删掉。假设这时有个新的系统E需要数据,这时系统A又要增加调用系统E的代码。为了降低这种强耦合,就可以使用MQ,系统A只需要把数据发送到MQ,其他系统如果需要数据,则从MQ中获取即可。
异步
。如图所示。一个客户端请求发送进来,系统A会调用系统B、C、D三个系统,同步请求的话,响应时间就是系统A、B、C、D的总和,也就是800ms。如果使用MQ,系统A发送数据到MQ,然后就可以返回响应给客户端,不需要再等待系统B、C、D的响应,可以大大地提高性能
。对于一些非必要的业务,比如发送短信,发送邮件等等,就可以采用MQ。削峰
。如图所示。这其实是MQ一个很重要的应用。假设系统A在某一段时间请求数暴增,有5000个请求发送过来,系统A这时就会发送5000条SQL进入MySQL进行执行,MySQL对于如此庞大的请求当然处理不过来,MySQL就会崩溃,导致系统瘫痪。如果使用MQ,系统A不再是直接发送SQL到数据库,而是把数据发送到MQ,MQ短时间积压数据是可以接受的,然后由消费者每次拉取2000条进行处理,防止在请求峰值时期大量的请求直接发送到MySQL导致系统崩溃。
三、RabbitMQ的特点
RabbitMQ是一款使用Erlang
语言开发的,实现AMQP(高级消息队列协议)
的开源消息中间件。首先要知道一些RabbitMQ的特点:
-
可靠性
。支持持久化,传输确认,发布确认等保证了MQ的可靠性。 -
灵活的分发消息策略
。这应该是RabbitMQ的一大特点。在消息进入MQ前由Exchange(交换机)进行路由消息。分发消息策略有:简单模式、工作队列模式、发布订阅模式、路由模式、通配符模式。 -
支持集群
。多台RabbitMQ服务器可以组成一个集群,形成一个逻辑Broker。 -
多种协议
。RabbitMQ支持多种消息队列协议,比如 STOMP、MQTT 等等。 -
支持多种语言客户端
。RabbitMQ几乎支持所有常用编程语言,包括 Java、.NET、Ruby 等等。 -
可视化管理界面
。RabbitMQ提供了一个易用的用户界面,使得用户可以监控和管理消息 Broker。 -
插件机制
。RabbitMQ提供了许多插件,可以通过插件进行扩展,也可以编写自己的插件。
四、RabbitMQ初の体验
1.windows安装RabbitMQ
一、整体思路
- 1.安装Erlang
- 2.安装RabbitMQ
- 3.激活RabbitMQ’s Management Plugin可视化插件
二、具体步骤
1. 安装Erlang
- 1.1 下载地址: https://www.erlang.org/downloads
- 1.2 运行otp_win64_24.2.exe,安装Erlang
- 1.3 设置环境变量ERLANG_HOME: 此电脑 -> 右键属性 -> 高级系统设置 -> 高级 -> 环境变量 -> 系统变量 -> 新建ERLANG_HOME
变量名:ERLANG_HOME
,变量值:D:\tool\erl-24.2
- 1.4 修改环境变量
Path
: 末尾追加:%ERLANG_HOME%\bin
,保存 - 1.5 打开命令行,输入
erl
,提示版本信息: Eshell V12.2 (abort with ^G),说明Erlang安装 成功
2. 安装RabbitMQ
- 2.1 下载地址: https://www.rabbitmq.com/install-windows.html,选择rabbitmq-server-3.9.11.exe
- 2.2 运行rabbitmq-server-3.9.11.exe,安装RabbitMQ
- 2.3 设置环境变量RABBITMQ_SERVER:此电脑 -> 右键属性 -> 高级系统设置 -> 高级 -> 环境变量 -> 系统变量 -> 新建RABBITMQ_SERVER
变量名:RABBITMQ_SERVER
,变量值:D:\tool\RabbitMQ Server\rabbitmq_server-3.9.11
- 2.4 修改
Path
: 追加:%RABBITMQ_SERVER%\sbin
- 2.5 命令行输入:
rabbitmqctl status
, 出现 - 说明
RabbitMQ
已安装成功,且已启动,但此时访问http://localhost:15672/发现无法访问,因为还未激活管理插件
3. 激活RabbitMQ’s Management Plugin可视化插件
- 3.1 进入到RabbitMQ安装目录,我的目录是:
D:\tool\RabbitMQ Server\rabbitmq_server-3.9.11
,进入sbin
目录,打开CMD命令行运行命令:rabbitmq-plugins.bat enable rabbitmq_management
,出现如下图片说明插件安装成功 - 3.2 验证: 浏览器访问: http://localhost:15672/
用户名 密码均为:guest
,登录
至此,windows安装RabbitMQ已全部完成
2.永远的Hello Word
下面,我们通过在Spring Boot应用中整合RabbitMQ,并实现一个简单的发送、接收消息的例子来对RabbitMQ有一个直观的感受和理解。
1.新建一个Spring Boot工程,命名为:“rabbitmq”。
2.pom.xml
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>com.dyh.rabbitmq</groupId>
<artifactId>rabbitmq</artifactId>
<version>1.0-SNAPSHOT</version>
<!--
这是 Spring Boot 的父级依赖,这样当前的项目就是 Spring Boot 项目了
spring-boot-starter-parent 是一个特殊的 starter,它用来提供相关的 Maven 默认依赖。
使用它之后,常用的包依赖可以省去 version 标签
-->
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.0.0.RELEASE</version>
<relativePath/>
</parent>
<dependencies>
<!--支持RabbitMQ-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-amqp</artifactId>
</dependency>
<!--测试-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
</dependencies>
</project>
3.application.yml
在application.yml
中配置关于RabbitMQ的连接和用户信息,用户可以回到上面的安装内容,在管理页面中创建用户。
spring:
application:
name: rabbitmq
rabbitmq:
host: 127.0.0.1
port: 5672
username: guest
password: guest
4.创建消息生产者Sender
。通过注入AmqpTemplate
接口的实例来实现消息的发送,AmqpTemplate
接口定义了一套针对AMQP协议的基础操作。在Spring Boot中会根据配置来注入其具体实现。在该生产者,我们会产生一个字符串,并发送到名为hello
的队列中。
package com.dyh.rabbitmq.producer;
import org.springframework.amqp.core.AmqpTemplate;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import java.util.Date;
/**
* @ClassName: Sender
* @author: dyh
* @since: 2021/12/27 15:43
*/
@Component
public class Sender {
@Autowired
private AmqpTemplate rabbitTemplate;
public void send() {
String context = "Hello Word " + new Date();
System.out.println("Sender : " + context);
rabbitTemplate.convertAndSend("hello", context);
}
}
5.创建消息消费者Receiver
。通过@RabbitListener
注解定义该类对hello
队列的监听,并用@RabbitHandler
注解来指定对消息的处理方法。所以,该消费者实现了对hello
队列的消费,消费操作为输出消息的字符串内容。
package com.dyh.rabbitmq.consumer;
import org.springframework.amqp.rabbit.annotation.RabbitHandler;
import org.springframework.amqp.rabbit.annotation.RabbitListener;
import org.springframework.stereotype.Component;
/**
* @ClassName: Receiver
* @author: dyh
* @since: 2021/12/27 15:46
*/
@Component
@RabbitListener(queues = "hello")
public class Receiver {
@RabbitHandler
public void process(String hello) {
System.out.println("Receiver : " + hello);
}
}
6.创建RabbitMQ的配置类RabbitConfig
,用来配置队列、交换器、路由等高级信息。这里我们以入门为主,先以最小化的配置来定义,以完成一个基本的生产和消费过程。
package com.dyh.rabbitmq.config;
import org.springframework.amqp.core.Queue;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
/**
* @ClassName: RabbitConfig
* @author: dyh
* @since: 2021/12/27 15:47
*/
@Configuration
public class RabbitConfig {
@Bean
public Queue helloQueue() {
return new Queue("hello");
}
}
7.创建应用主类:
package com.dyh.rabbitmq;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
/**
* @ClassName: Application
* @author: dyh
* @since: 2021/12/27 15:51
*/
@SpringBootApplication
public class Application {
public static void main(String[] args) {
SpringApplication.run(Application.class, args);
}
}
8.创建单元测试类,用来调用消息生产:
package com.dyh.rabbitmq;
import com.dyh.rabbitmq.producer.Sender;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.SpringBootConfiguration;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
/**
* @ClassName: ApplicationTests
* @author: dyh
* @since: 2021/12/27 15:52
*/
@RunWith(SpringJUnit4ClassRunner.class)
@SpringBootTest(classes = Application.class)
public class ApplicationTests {
@Autowired
private Sender sender;
@Test
public void hello() throws Exception {
sender.send();
}
}
9.测试
完成程序编写之后,下面开始尝试运行。首先确保RabbitMQ Server已经开始,然后进行下面的操作:
1.启动应用主类,从控制台中,我们看到如下内容,程序创建了一个访问127.0.0.1:5672
中guest
的连接。
o.s.a.r.c.CachingConnectionFactory : Created new connection: rabbitConnectionFactory#37c7595:0/SimpleConnection@45064a6d [delegate=amqp://guest@127.0.0.1:5672/, localPort= 61365]
同时,我们通过RabbitMQ的控制面板,可以看到Connection和Channels中包含当前连接的条目。
2.运行单元测试类,我们可以看到控制台中输出下面的内容,消息被发送到了RabbitMQ Server
的hello
队列中。
Sender : Hello Word Mon Dec 27 15:58:34 CST 2021
3.切换到应用主类的控制台,我们可以看到类似如下输出,消费者对hello
队列的监听程序执行了,并输出了接受到的消息信息。
Receiver : Hello Word Mon Dec 27 15:58:34 CST 2021
通过上面的示例,我们在Spring Boot应用中引入spring-boot-starter-amqp
模块,进行简单配置就完成了对RabbitMQ
的消息生产和消费
的开发内容。然而在实际应用中,我们还有很多内容没有演示,这里不做更多的讲解,读者可以自行查阅RabbitMQ的官方教程,有更全面的了解。
五、RabbitMQ中的组成部分
- Broker:消息队列服务进程。此进程包括两个部分:Exchange和Queue。
- Exchange:消息队列交换机。按一定的规则将消息路由转发到某个队列。
- Queue:消息队列,存储消息的队列。
- Producer:消息生产者。生产方客户端将消息同交换机路由发送到队列中。
- Consumer:消息消费者。消费队列中存储的消息。
这些组成部分是如何协同工作的呢,大概的流程如下,请看下图:
- 消息生产者连接到RabbitMQ Broker,创建connection,开启channel。
- 生产者声明交换机类型、名称、是否持久化等。
- 生产者发送消息,并指定消息是否持久化等属性和routing key。
- exchange收到消息之后,
根据routing key路由到跟当前交换机绑定的相匹配的队列
里面。 - 消费者监听接收到消息之后开始业务处理。
六、Exchange的四种类型以及用法
从上面的工作流程可以看出,实际上有个关键的组件Exchange,因为消息发送到RabbitMQ后首先要经过Exchange路由才能找到对应的Queue
。
实际上Exchange类型有四种,根据不同的类型工作的方式也有所不同。Direct Exchange
、Fanout exchange
、Topic exchange
、Headers exchange
。
1.Direct Exchange
见文知意,直连交换机意思是此交换机需要绑定一个队列,要求该消息与一个特定的路由键完全匹配
。简单点说就是一对一的,点对点的发送。
2.Fanout exchange
这种类型的交换机需要将队列绑定到交换机上。一个发送到交换机的消息都会被转发到与该交换机绑定的所有队列上
。很像子网广播,每台子网内的主机都获得了一份复制的消息。简单点说就是发布订阅。
3.Topic Exchange
直接翻译的话叫做主题交换机,如果从用法上面翻译可能叫通配符交换机会更加贴切。这种交换机是使用通配符去匹配,路由到对应的队列。通配符有两种:"*"
、 "#"
。需要注意的是通配符前面必须要加上"."
符号。
*
符号:有且只匹配一个词。比如 a.*
可以匹配到"a.b"
、"a.c"
,但是匹配不了"a.b.c"
。
#
符号:匹配一个或多个词。比如"rabbit.#"
既可以匹配到"rabbit.a.b"
、"rabbit.a"
,也可以匹配到"rabbit.a.b.c"
。
比较常用的就是以上三种:直连(DirectExchange),发布订阅(FanoutExchange),通配符(TopicExchange)。熟练运用这三种交换机类型,基本上可以解决大部分的业务场景。
实际上稍微思考一下,可以发现通配符(TopicExchange)这种模式其实是可以达到直连(DirectExchange)和发布订阅(FanoutExchange)这两种的效果的。
4.Headers Exchange
这种交换机用的相对没这么多。它跟上面三种有点区别,它的路由不是用routingKey进行路由匹配,而是在匹配请求头中所带的键值进行路由
。如图所示:
创建队列需要设置绑定的头部信息,有两种模式:全部匹配
和部分匹配
。如上图所示,交换机会根据生产者发送过来的头部信息携带的键值去匹配队列绑定的键值,路由到对应的队列。