😎 知识点概览
为了方便后续回顾该项目时能够清晰的知道本章节讲了哪些内容,并且能够从该章节的笔记中得到一些帮助,所以在完成本章节的学习后在此对本章节所涉及到的知识点进行总结概述。
本章节为【学成在线】项目的 day05
的内容
-
RabbitMQ
的基本应用场景 - 使用原生
RabbitMQ
库构建生产者与消费者模型 - 整合
Springboot
实现RabbitMQ
生产者与消费者模型
目录
内容会比较多,小伙伴们可以根据目录进行按需查阅。
文章目录
一、需求分析
业务流程如下:
1、管理员进入管理界面点击 “页面发布”,前端请求 cms
页面发布接口。
2、cms 页面发布接口执行页面静态化,并将静态化页面(html
文件)存储至GridFS
中。
3、静态化成功后,向消息队列发送页面发布的消息。页面发布的最终目标是将页面发布到服务器。通过消息队列将页面发布的消息发送给各个服务器。
4、消息队列负责将消息发送给各各服务器上部署的 Cms Client (Cms客户端)。在服务器上部署 Cms Client(Cms客户端),客户端接收消息队列的通知。
5、每个接收到页面发布消息的 Cms Client
从 GridFS
获取 Html
页面文件,并将 Html
文件存储在本地服务器。CmsClient
根据页面发布消息的内容请求 GridFS
获取页面文件,存储在本地服务器 。
二、初识RabbitMQ
要实现上边页面发布的功能,有一个重要的环节就是由消息队列将页面发布的消息通知给各各服务器。
本节的教学目标是对MQ的研究:
1、理解MQ的应用场景
2、理解MQ常用的工作模式
0x01 简单的介绍
RabbitMQ 简介
MQ全称为 Message Queue,即消息队列, RabbitMQ
是由 erlang
语言开发,基于**AMQP(Advanced Message Queue 高级消息队列协议)**协议实现的消息队列,它是一种应用程序之间的通信方法,消息队列在分布式系统开发中应用非常广泛。
RabbitMQ官方地址:http://www.rabbitmq.com/
开发中消息队列通常有如下应用场景:
-
任务异步处理
将不需要同步处理的并且耗时长的操作由消息队列通知消息接收方进行异步处理。提高了应用程序的响应时间。
-
应用程序解耦合
MQ相当于一个中介,生产方通过MQ与消费方交互,它将应用程序进行解耦合。
-
市场上还有哪些消息队列?
ActiveMQ,RabbitMQ,ZeroMQ,Kafka,MetaMQ,RocketMQ、Redis。 -
为什么使用RabbitMQ呢?
1、使得简单,功能强大。
2、基于AMQP
协议。
3、社区活跃,文档完善。
4、高并发性能好,这主要得益于Erlang
语言。
5、Spring Boot 默认已集成RabbitMQ
其它相关知识
AMQP是什么 ?
AMQP
是一套公开的消息队列协议,最早在2003年被提出,它旨在从协议层定义消息通信数据的标准格式,为的就是解决 MQ
市场上协议不统一的问题。RabbitMQ
就是遵循 AMQP
标准协议开发的MQ服务。
JMS是什么 ?
JMS
是 java
提供的一套消息服务API标准,其目的是为所有的 java
应用程序提供统一的消息通信的标准,类似 java
的 jdbc
,只要遵循 jms
标准的应用程序之间都可以进行消息通信。它和 AMQP
有什么 不同,jms
是java语言专属的消息服务标准,它是在api层定义标准,并且只能用于 java
应用;而 AMQP
是在协议层定义的标准,是跨语言的 。
0x02 快速入门
RabbitMQ 的工作原理
下图是 RabbitMQ
的基本结构
组成部分说明如下:
- Broker:消息队列服务进程,此进程包括两个部分:
Exchange
和Queue
。 - Exchange:消息队列交换机,按一定的规则将消息路由转发到某个队列,对消息进行过滤。
- Queue:消息队列,储存消息的队列,消息到达队列并转发给指定的消费方。
- Producer:消息生产者,即生产方客户端,生产方客户端将消息发送到
MQ
。 - Consumer:消息消费者,即消费方客户端,接收
MQ
转发的消息。
消息 发布 与 接收 流程:
-----发送消息-----
1、生产者和 Broker
建立TCP连接。
2、生产者和 Broker
建立通道。
3、生产者通过通道消息发送给 Broker
,由 Exchange
将消息进行转发。
4、Exchange
将消息转发到指定的 Queue
(队列)
----接收消息-----
1、消费者和 Broker
建立TCP连接
2、消费者和 Broker
建立通道
3、消费者监听指定的 Queue
(队列)
4、当有消息到达 Queue
时 Broker
默认将消息推送给消费者。
5、消费者接收到消息。
安装 RabbitMQ
1、下载并安装
RabbitMQ由 Erlang
语言开发,Erlang
语言用于并发及分布式系统的开发,在电信领域应用广泛,OTP(OpenTelecom Platform)作为 Erlang
语言的一部分,包含了很多基于 Erlang
开发的中间件及工具库,安装 RabbitMQ
需要安装 Erlang/OTP,并保持版本匹配,如下图:
本项目使用 Erlang/OTP 20.3
版本和 RabbitMQ3.7.3
版本。
官网 RabbitMQ
的下载地址:http://www.rabbitmq.com/download.html
下载erlang
地址如下:http://erlang.org/download/otp_win64_20.3.exe
erlang安装完成需要配置erlang环境变量: ERLANG_HOME=D:\Program Files\erl9.3 在path中添
加%ERLANG_HOME%\bin;
安装RabbitMQ
下载地址 https://github.com/rabbitmq/rabbitmq-server/releases/tag/v3.7.3
这里要注意 rabbitMQ的安装路径
2、启动
安装成功后会自动创建RabbitMQ服务并且启动。
-
在系统path变量中添加
rabbitMQ
的环境变量:D:\[rabbitMQ的安装路径]\sbin;
-
添加环境变量后,按下
Win + X
以管理员身份运行powershell 或者 cmd -
运行
rabbitmq-plugins.bat enable rabbitmq_management
命令 -
运行
rabbitmq-service.bat stop
和rabbitmq-service.bat start
重启 rabbitMQ -
启动成功,访问 http://localhost:15672 登录Rabbit MQ,初始账号密码为 guest/guest
3、注意事项
1、安装 erlang
和 rabbitMQ
以管理员身份运行。
2、当卸载重新安装时会出现 RabbitMQ
服务注册失败,此时需要进入注册表清理 erlang
搜索RabbitMQ、ErlSrv,将对应的项全部删除。
Hello World
以下过程我们参考官方教程(http://www.rabbitmq.com/getstarted.html)测试hello world:
1、搭建环境
java client
生产者和消费者都属于客户端,rabbitMQ的java客户端如下:
我们先用 rabbitMQ
官方提供的 java client
测试,目的是对 RabbitMQ
的交互过程有个清晰的认识。
参考 :https://github.com/rabbitmq/rabbitmq-java-client/
创建maven工程
创建生产者工程和消费者工程,分别加入RabbitMQ java client
的依赖。
test-rabbitmq-producer:生产者工程
test-rabbitmq-consumer:消费者工程
依赖配置如下:
<dependencies>
<dependency>
<groupId>com.rabbitmq</groupId>
<artifactId>amqp-client</artifactId>
<version>4.0.3</version><!--此版本与spring boot 1.5.9版本匹配-->
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-logging</artifactId>
</dependency>
</dependencies>
2、生产者
在生产者工程下的单元测试内创建测试类如下
import com.rabbitmq.client.ConnectionFactory;
import com.rabbitmq.client.Channel;
import com.rabbitmq.client.Connection;
import java.io.IOException;
import java.util.concurrent.TimeoutException;
public class Producer01 {
//队列名称
private static final String QUEUE = "helloworld2";
public static void main(String[] args) throws IOException, TimeoutException {
Connection connection = null;
Channel channel = null;
try {
//构建连接工厂,并设置一些基本的链接信息
ConnectionFactory factory = new ConnectionFactory();
factory.setHost("localhost");
factory.setPort(5672);
factory.setUsername("guest");
factory.setPassword("guest");
//rabbitMQ默认的虚拟机名称为“/”,虚拟机相当于一个独立的mq服务
factory.setVirtualHost("/");
//创建与RabbitMQ服务的TCP连接
connection = factory.newConnection();
//创建与Exchange的通道,每个连接可以创建多个通道,每个通道代表一个会话任务
channel = connection.createChannel();
/**
* 声明队列,如果Rabbit中没有此队列,将自动创建
* String queue, boolean durable, boolean exclusive, boolean autoDelete,Map<String, Object> arguments
*
* param1:队列名称
* param2:是否持久化
* param3:队列是否独占此连接
* param4:队列不再使用时是否自动删除此队列
* param5:队列参数
*/
channel.queueDeclare(QUEUE,true,false,false,null);
String message = "hello world 小明" + System.currentTimeMillis();
/**
* 消息发布方法
消息发布方法
* param1:Exchange的名称,如果没有指定,则使用Default Exchange
* param2:routingKey,消息的路由Key,是用于Exchange(交换机)将消息转发到指定的消息队列
* param3:消息包含的属性
* param4:消息体
* 这里没有指定交换机,消息将发送给默认交换机,每个队列也会绑定那个默认的交换机,但是不能显示绑定或解除绑定
* 默认的交换机,routingKey等于队列名称
*/
channel.basicPublish("",QUEUE,null,message.getBytes());
System.out.println("Send Message is: ' " + message + " '");
}catch ( Exception ex){
ex.printStackTrace();
}finally {
//先关闭通道,再关闭连接
if(channel != null){
channel.close();
}
if(channel != null){
connection.close();
}
}
}
}
运行生产者后,RabbitMQ
后台会接收到一条等待消费的消息
3、消费者
import com.rabbitmq.client.*;
import java.io.IOException;
import java.util.concurrent.TimeoutException;
public class Consumer01 {
private static final String QUEUE = "helloworld2";
public static void main(String[] args) throws IOException, TimeoutException {
ConnectionFactory factory = new ConnectionFactory();
factory.setHost("localhost");
factory.setPort(5672);
factory.setUsername("guest");
factory.setPassword("guest");
//rabbitMQ默认的虚拟机名称为“/”,虚拟机相当于一个独立的mq服务
factory.setVirtualHost("/");
Connection connection = factory.newConnection();
Channel channel = connection.createChannel();
//声明队列
/** 参数:String queue, boolean durable, boolean exclusive, boolean autoDelete,
Map<String, Object> arguments
* 参数明细:
* 1、queue 队列名称
* 2、durable 是否持久化,如果持久化,mq重启后队列还在
* 3、exclusive 是否独占连接,队列只允许在该连接中访问,如果connection连接关闭,队列则自动删除,可用于临时队列的创建
* 4、autoDelete 自动删除,队列不再使用时是否自动删除此队列,如果将此参数和exclusive共同为true,就可以实现临时队列
* 5、argmuacnts,可以设置一个队列扩展参数,比如:可设置存活的时间
*/
channel.queueDeclare(QUEUE, true, false, false, null);
//定义消费方法
DefaultConsumer defaultConsumer = new DefaultConsumer(channel) {
/**
* 消费者接收消息调用此方法
* @param consumerTag 消费者的标签,在channel.basicConsume()去指定
* @param envelope 消息包的内容,可从中获取消息id,消息routingkey,交换机,消息和重传标志
(收到消息失败后是否需要重新发送)
* @param properties 消息属性
* @param body 消息内容
* @throws IOException
1、发送端操作流程
1)创建连接
2)创建通道
3)声明队列
4)发送消息
2、接收端
1)创建连接
2)创建通道
3)声明队列
4)监听队列
5)接收消息
*/
@Override
public void handleDelivery(String consumerTag, Envelope envelope, AMQP.BasicProperties properties, byte[] body) throws IOException {
//交换机
String exchange = envelope.getExchange();
//路由key
String routingKey = envelope.getRoutingKey();
//消息id
long deliveryTag = envelope.getDeliveryTag();
//消息内容
String msg = new String(body, "utf-8");
System.out.println("receive message.." + msg);
}
};
/**
* 监听队列String queue, boolean autoAck,Consumer callback
* 参数明细
* 1、queue 队列名称
* 2、autoAck 是否自动回复,设置为true为表示消息接收到自动向mq回复接收到了,mq接收到回复会删除消息,设置为false则需要手动回复
* 3、callback 消费消息的方法,消费者接收到消息后调用此方法