RabbitMQ
MQ全称为Message Queue,即消息队列, RabbitMQ是由erlang语言(主要用于开发高并发程序的)开发,基于AMQP(Advanced Message Queue 高级消息队列协议)协议实现的消息队列,它是一种应用程序之间的通信方法,消息队列在分布式系统开 发中应用非常广泛。
开发中消息队列通常有如下应用场景
-
任务异步 处理。
将不需要同步处理并且耗时长的操作由消息队列通知消息接收方进行异步处理,提高了应用程序的响应时间。 -
应用程序解耦合
MQ相当于一个中介,生产方通过MQ与消息方交互,它将应用程序进行解耦合。
其他的消息队列
除了RabbitMQ之外,还有一些其他的消息中间件,比如
ActivateMQ,RabbitMQ,ZeroMQ,Kafka(这个是大数据领域专用的消息队列),MetaMQ,RocketMQ,Redis(也可以作为MQ)
在这里主要是记录一下RabbitMQ这个消息中间件的使用以及常见操作。
我们首先看一下为什么要使用RabbitMQ消息中间件?
- 使用简单,功能强大
- 基于AMQP协议
- 社区活跃,文档完善
- 高并发性能好,这主要得益于Erlang语言
- Spring Boot中默认已经集成RabbitMQ。我们如果使用spring Boot开发程序,可以直接使用里面集成的RabbitMQ
补充知识
我们首先看一下AMQP协议
AMQP是一个提供统一消息服务的应用层标准高级消息队列协议,是应用层协议的一个开发标准,为面向消息的中间件设计。基于此协议的客户端与消息中间件可传递消息,并不受客户端中间件不同产品,不同开发语言等条件限制。
JAVA消息服务
Java消息服务也称为JMS应用程序接口是一个Java平台中关于面向消息中间件的API,用于在两个应用程序之间,或者是分布式系统中发送消息,进行异步通信,Java消息服务是一个与具体平台无关的api ,绝大多数的MOM提供商都队JMS提供支持。
JMS是java提供的一套消息服务API标准,其目的是为所有的java应用程序提供统一的消息通信的标准,类似java的 jdbc,只要遵循jms标准的应用程序之间都可以进行消息通信。它和AMQP有什么 不同,jms是java语言专属的消 息服务标准,它是在api层定义标准,并且只能用于java应用;而AMQP是在协议层定义的标准,是跨语言的
RabbitMQ的工作原理
我们通过一张图也看一下,RabbitMQ的组成结构以及工作原理
Producer :是生产者,要发送消息的一方,要发送消息肯定需要与MQ建立消息socket连接Connection(Connection中的Channel可以看成是会话通道),MQ接收到消息之后,肯定是需要把消息转发给消费者Consumer
Consumer:Consumer也需要跟MQ建立一个连接,监听消息
Exchange交换机,一个交换机可以有多个消息队列,当消息过来的时候,由交换机转发给消息队列queue
组成部分说明如下:
- 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这一个插件的简单介绍,下面我们通过一个例子来看一下RabbitMQ是如何使用的。
在使用之前,我们首先需要安装RabbitMQ
安装RabbitMQ
在这里我是在阿里云服务器上进行安装的 环境是CentOS7.5
安装步骤:
安装依赖文件
yum -y install gcc glibc-devel make ncurses-devel openssl-devel xmlto perl wget
若不执行以上的依赖文件的安装,则在执行./configure --prefix=/usr/local/erlang的过程中,最后两行会出现如下错误:
configure: error: No curses library functions found
configure: error: /bin/sh '/home/ding/otp_src_20.3/erts/configure' failed for erts
因在安装erlang的make过程中会用到java命令,故安装erlang前请检查系统中是否存在jdk,若没有需先安装jdk
安装erlang语言环境:
wget http://www.erlang.org/download/otp_src_20.3.tar.gz #下载erlang包
tar -xvf otp_src_20.3.tar.gz #解压
cd otp_src_20.3/ #切换到安装路径
./configure --prefix=/usr/local/erlang #生产安装配置
make && make install #编译安装
配置erlang环境变量
vi /etc/profile #在底部添加以下内容
#set erlang environment
ERL_HOME=/usr/local/erlang
PATH=$ERL_HOME/bin:$PATH
export ERL_HOME PATH
source /etc/profile #使以上配置生效
测试一下是否安装成功,在控制台输入命令erl
erl #如果进入erlang的shell则证明安装成功,退出即可。
安装RabbitMQ
下载安装(也可在http://www.rabbitmq.com/releases/rabbitmq-server/下选择对应的版本)
cd /usr/local #切换到计划安装RabbitMQ的目录,我这里放在/usr/local
wget https://dl.bintray.com/rabbitmq/all/rabbitmq-server/3.7.4/rabbitmq-server-generic-unix-3.7.4.tar.xz
# wget http://www.rabbitmq.com/releases/rabbitmq-server/v3.7.4/rabbitmq-server-generic-unix-3.7.14.tar.xz (此下载源失效)
xz -d rabbitmq-server-generic-unix-3.7.4.tar.xz
tar -xvf rabbitmq-server-generic-unix-3.7.4.tar
mv rabbitmq_server-3.7.4/ rabbitmq
配置rabbitmq环境变量:
vi /etc/profile
在文件末尾添加
#set rabbitmq environment
export PATH=$PATH:/usr/local/rabbitmq/sbin
保存退出
source /etc/profile
启动服务
rabbitmq-server -detached //启动rabbitmq,-detached代表后台守护进程方式启动。
如果启动失败,可尝试
nohup rabbitmq-server restart >rabbitmqLOG.log 2>&1 &
(停止rabbitmq:rabbitmqctl stop)
查看状态,如果显示如下截图说明安装成功:
rabbitmqctl status
配合网页插件
首先创建目录,否则可能报错:
mkdir /etc/rabbitmq
然后启用插件:
rabbitmq-plugins enable rabbitmq_management
配置防火墙:
配置linux 端口 15672 网页管理 5672 AMQP端口:(阿里云服务器只需要将15672、5672端口加入安全组即可,无需以下3步操作)
firewall-cmd --permanent --add-port=15672/tcp
firewall-cmd --permanent --add-port=5672/tcp
systemctl restart firewalld.service
现在你在浏览器中输入服务器IP:15672 就可以看到RabbitMQ的WEB管理页面了,是不是很兴奋,可是你没有账号密码,别急
配置访问账号密码的和权限
默认网页是不允许访问的,需要增加一个用户修改一下权限,代码如下:
rabbitmqctl add_user super super #添加用户,后面两个参数分别是用户名和密码,我这都用super了。
rabbitmqctl set_permissions -p / super ".*" ".*" ".*" #添加权限
rabbitmqctl set_user_tags super administrator #修改用户角色
如果使用的是阿里云服务器,还需要在阿里云控制台安全组里开启5672和15672端口。
安装延时任务插件(非必须)
linux安装rabbitmq_delayed_message_exchange插件(可通过该插件的x-delay-message实现延时消息队列)
检查本地是否安装rabbitmq_delayed_message_exchange
rabbitmq-plugins list
wget wget https://dl.bintray.com/rabbitmq/community-plugins/3.7.x/rabbitmq_delayed_message_exchange/rabbitmq_delayed_message_exchange-20171201-3.7.x.zip
unzip,解压到/usr/local/rabbitmq/plugins/rabbitmq_delayed_message_exchange-20171201-3.7.x.ez
如果提示找不到命令,也可通过https://dl.bintray.com/rabbitmq/community-plugins/3.7.x/rabbitmq_delayed_message_exchange/下载压缩文件到本地,解压后在将rabbitmq_delayed_message_exchange-20171201-3.7.x.ez上传到/usr/local/rabbitmq/plugins/目录下
安装插件
rabbitmq-plugins enable rabbitmq_delayed_message_exchange
出现started 1 plugins.代表成功
RabbitMQ的使用
环境搭建完成,下面我们使用一个简单的入门案例
首先创建一个maven工程
创建生产者和消费者两个工程,分别在两个工程的prom文件加入RabbitMQ Java client 的依赖。
<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-amqp</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-logging</artifactId>
</dependency>
</dependencies>
然后在生产者中进行简单案例的编写,由生产者往消息队列中存放数据。
在实际的 开发中我们可以通过springBoot中集成过的消息队列通过注解的方式进行实现,但是在这里用最原始的连接建立开始进行代码的实现
我们通过Java创建两个类,生产者和消费者,生产者产生消息放入到消息队列中,消费者从消息队列中取出消息。
首先编写生产者
package com.xuecheng.test.rabbitmq;
import com.rabbitmq.client.Channel;
import com.rabbitmq.client.Connection;
import com.rabbitmq.client.ConnectionFactory;
import java.io.IOException;
import java.util.Queue;
import java.util.concurrent.TimeoutException;
/**
* rabbitMQ入门程序
*/
public class Producer01 {
//建立一个队列
private static final String QUEUE = "helloword2";
public static void main(String[] args) {
//通过连接工厂创建新的连接和MQ建立连接
ConnectionFactory connectionFactory = new ConnectionFactory();
connectionFactory.setHost("120.2.2.240");
connectionFactory.setPort(5672);
connectionFactory.setUsername("super");
connectionFactory.setPassword("super");
//设置虚拟机,一个MQ的服务可以设置多个虚拟机,每个虚拟机相当于一个独立的MQ
connectionFactory.setVirtualHost("/");//默认的/ 虚拟机
//建立新连接
Connection connection=null;
try {
connection = connectionFactory.newConnection();
//创建会话通道,生产者和MQ服务所有通信都是在chanel通道中进行完成的
Channel channel = connection.createChannel();
//声明队列,如果队列在MQ中没有则要创建
/**
* 参数声明
* 1, queue 队列名称
* 2, durable 是否持久化,如果持久化,mq重启后队列还在
* 3, exclusive 是否独占连接,队列只允许在该连接中访问,如果连接关闭队列自动删除,如果将此参数设置true,可用于临时队列的创建
* 也就是只,消息重启队列就丢失了
* 4, autoDelete 自动删除,连接不使用的时候自动删除队列,如果将此参数和exclusive参数设置为true就可以实现临时队列,队列不用就自动删除
* argument 参数,可以设置一个队列的扩展参数,比如:可以设置存活时间
*/
channel.queueDeclare(QUEUE,true,false,false,null);
//发送消息
/**
* 参数明细
* 1, exclusive , 交换机,如果不指定将使用mq默认交换机,设置为""
* 2, routingKey ,路由Key,交换机根据路由key来讲消息转发到指定的队列,如果使用默认交换机,routingKey设置为队列的名称
* 3, props ;消息的属性
* 4, body 消息内容
*/
//定义一个消息的内容
String message = "hello word 河南ZZU";
channel.basicPublish("",QUEUE,null,message.getBytes());
System.out.println("sent to MQ "+message);
} catch (IOException e) {
e.printStackTrace();
} catch (TimeoutException e) {
e.printStackTrace();
}
}
}
通过以上代码,我们可以简单的总结一下生产者的生产过程
总结生产者,产生消息的创建过程
- 发送端操作流程
- (1)创建连接
- (2)创建通道
- (3)声明队列
- (4)发送消息
消费者
接收端
- (1)创建连接
- (2)创建通道
- (3)声明队列
- (4)监听队列
- (5)接受消息
- (6)Ack回复
消费者代码实现如下:
package com.xuecheng.test.rabbitMQ;
import com.rabbitmq.client.*;
import java.io.IOException;
import java.util.concurrent.TimeoutException;
public class Consumer01 {
//建立一个队列
private static final String QUEUE = "helloword2";
public static void main(String[] args) {
//通过连接工厂创建新的连接和MQ建立连接
ConnectionFactory connectionFactory = new ConnectionFactory();
connectionFactory.setHost("120.xx.xx.240");
connectionFactory.setPort(5672);
connectionFactory.setUsername("super");
connectionFactory.setPassword("super");
//设置虚拟机,一个MQ的服务可以设置多个虚拟机,每个虚拟机相当于一个独立的MQ
connectionFactory.setVirtualHost("/");//默认的/ 虚拟机
//建立新连接
Connection connection=null;
Channel channel =null;
try {
connection = connectionFactory.newConnection();
//创建会话通道,生产者和MQ服务所有通信都是在chanel通道中进行完成的
channel = connection.createChannel();
//声明队列,如果队列在MQ中没有则要创建
/**
* 参数声明
* 1, queue 队列名称
* 2, durable 是否持久化,如果持久化,mq重启后队列还在
* 3, exclusive 是否独占连接,队列只允许在该连接中访问,如果连接关闭队列自动删除,如果将此参数设置true,可用于临时队列的创建
* 也就是只,消息重启队列就丢失了
* 4, autoDelete 自动删除,连接不使用的时候自动删除队列,如果将此参数和exclusive参数设置为true就可以实现临时队列,队列不用就自动删除
* argument 参数,可以设置一个队列的扩展参数,比如:可以设置存活时间
*/
channel.queueDeclare(QUEUE,true,false,false,null);
//实现消费方法
DefaultConsumer defaultConsumer = new DefaultConsumer(channel){
//当接受到消息之后,此方法将被调用
/**
* 将接受到消息后此方法将被调用
* @param consumerTag 消费者标签,将表示消费者的,如果想设置可以在监听队列的时候设置
* @param envelope 信封,通过envelope
* @param properties 消息属性
* @param body 消息内容
* @throws IOException
*/
@Override
public void handleDelivery(String consumerTag, Envelope envelope, AMQP.BasicProperties properties, byte[] body) throws IOException {
// 交换机
String exchange = envelope.getExchange();
// 消息ID,mq在channel中用来表示消息的id,
long deliveryTag = envelope.getDeliveryTag();
//消息内容
String message = new String(body,"utf-8");
System.out.println("接收到消息"+message);
}
};
//监听队列
/**
* 1. queue 队列名称
* 2 autoAck 自动回复,当消费者接受到消息后要告诉mq消息已经接收到。如果将此参数设置为true表示会自动回复mq吗,如果是设置为false则需要编辑实现回复
* 如果不回复,则一直存在在消息队列中
* 3, callback 消费方法,当消费者接受到消息要执行的方法
*
*
*/
channel.basicConsume(QUEUE,true,defaultConsumer);
} catch (IOException e) {
e.printStackTrace();
} catch (TimeoutException e) {
e.printStackTrace();
}
}
}
在上面的代码中我们可以使用生产者发送消息,运行代码消息进消息队列中,在web客户端在中我们可以看到生产者生产出的消息在消息队列中,等待消费者从中取出数据,消费者从消息队列中取出数据。
以上就是消息队列中的一个简单使用。
RabbitMQ有好几种工作模式,通过在平时使用RabbitMQ的过程中,我们会根据业务场景的不同使用不同的工作模式,下一章节对RabbitMQ的工作模式进行说明。