1、引言
什么是MQ
MQ(Message Quene):消息队列,通过典型的生产者和消费者模型不断向消息队列中生产消息,消费者不断从队列中获取消息。因为消息的生产和消费是异步的,而且只关系消息的发送和接收,没有业务逻辑的侵入,轻松地实现系统间解耦。别名为消息中间件,通过利用高效可靠的消息传递机制进行平台无关的数据交流,并给予数据通信进行分布式系统的集成
AMQP 协议
AMQP(advanced message queuing protocol)在2003年时被提出,最早用于解决金融领不同平台之间的消息传递交互问题。顾名思义,AMQP是一种协议,更准确的说是一种binary wire-level protocol(链接协议)。这是其和JMS的本质差别,AMQP不从API层进行限定,而是直接定义网络交换的数据格式。这使得实现了AMQP的provider天然性就是跨平台的。以下是AMQP协议模型:
不同MQ特点
- ActiveMQ:是Apache出品,是最流行,能力强劲的开源消息总线。它是一个完全支持JMS规范的消息中间件。有丰富的API,多种集群架构模式让ActiveMQ称为老牌成熟的消息中间件,中小企业使用广泛
- Kafka:是LinkedIn开源的分布式发布-订阅消息系统,目前属于Apache顶级项目。主要特点是基于Pull的模式处理消息消费,追求吞吐量。一开始的目的就是用于日志收集和传输,0.8版本之后开始支持复制,不支持事务,对消息的重复、丢失、错误没有严格要求,适合产生大量数据的互联网服务的数据收集业务
- RocketMQ:是阿里开源的消息中间件,纯java开发,具有高吞吐量、高可用性、适合大规模分布式系统应用的特点。RocketMQ起源于Kafka,对消息的可靠传输及事务性做了优化,在阿里被广泛用于交易、充值、流计算、消息推送、日志流式处理、binglog分发等场景
- RabbitMQ:使用Erlang语言开发的消息队列系统。基于AMQP协议来实现。主要特征是面向消息、队列、路由(包括点对点和发布/订阅)、可靠性、安全。AMQP协议更多用在企业系统内对数据一致性、稳定性和可靠性要求很高的场景,对性能和吞吐量的要求在其次
RabbitMQ比Kafka更可靠,Kafka更适合IO高吞吐的处理,一般应用在大数据日志处理或对实时性(少量延迟),可靠性(少量丢失数据)要求稍低的场景使用,比如ELK日志收集
RabbitMQ安装
RabbitMQ下载 (注意与erlang版本对应关系)
# 1.将安装包上传到服务器
erlang-23.3.1-1.el7.x86_64.rpm
rabbitmq-server-3.8.13-1.el7.noarch.rpm
# 2.安装erlang依赖包
yum install -y erlang-23.3.1-1.el7.x86_64.rpm
# 3.安装rabbitmq
yum install -y rabbitmq-server-3.8.13-1.el7.noarch.rpm
# 4.配置文件
#见下方配置文件说明
# 5.修改配置文件
loopback_users.guest = false
# 6.启动rabbitmq中的插件管理
rabbitmq-plugins enable rabbitmq_management
# 7.启动rabbitmq
systemctl start rabbitmq-server # 启动rabbitmq服务
systemctl restart rabbitmq-server # 重启服务
systemctl stop rabbitmq-server # 停止服务
# 8.查看rabbitmq状态
systemctl status rabbitmq-server
# 9.访问rabbitmq的web管理界面
#开放端口访问
firewall-cmd --add-port=15672/tcp --permanent
firewall-cmd --reload
http://192.168.232.134:15672/ #访问
# 10.初始登录
guest guest
rabbitmq-server-3.8.13配置文件
安装之后/etc/rabbitmq/目录下没有rabbitmq的配置文件,在/usr/share/doc/rabbitmq-server-3.8.13/目录下也没有提供配置文件模板
可以到github上下载将其拷贝到目录下
https://github.com/rabbitmq/rabbitmq-server/tree/v3.8.x/deps/rabbit/docs
因为没有多少配置,此处直接在/etc/rabbitmq/目录下新建rabbit.conf,添加
loopback_users.guest = false
更多详细配置查看Configuration — RabbitMQ
登录成功显示
2、RabbitMQ配置
web界面连接rabbitmq端口:15672
Java连接rabbitmq端口:5672
RabbitMQ管理命令行
# 1.服务启动相关
systemctl start|restart|stop|status rabbitmq-server
# 2.管理命令行 用来在不使用web管理界面情况下命令操作RabbitMQ
rabbitmqctl help #查看支持的命令
# 3.插件管理命令行
rabbitmq-plugins enable|list|disable
web管理界面介绍
导航栏介绍
常见概念
Server(broker): 接受客户端连接,实现AMQP消息队列和路由功能的进程。
Virtual Host:虚拟主机,类似于权限控制组,一个Virtual Host里面可以有若干个Exchange和Queue,但是权限控制的最小粒度是Virtual Host
Connections:无论生产者还是消费者,都需要与RabbitMQ建立连接后才能完成消息的生产和消费,在这里可以查看连接情况。对于RabbitMQ而言,其实就是一个位于客户端和Broker之间的TCP连接。
Channels:通道,建立连接后会形成通道,消息的传递获取依赖于通道
Exchanges:交换机,用来实现消息的路由。接收生产者发送的消息,并根据一定规则将消息路由给服务器中的队列
Message Queues:消息队列,消息存放在队列中,等待消费,消费后被移除队列
Message:由Header和Body组成。Header是由生产者添加的各种属性的集合,包括Message是否被持久化、由哪个Message Queue接受、优先级是多少等。而Body是真正需要传输的APP数据。
Admin用户管理和虚拟主机管理
创建用户
Tags选项用户可选类型
- Admin:超级管理员,可登录管理控制台,可查看所有信息,并且可以对用户,策略(policy)进行操作
- Monitoring:监控者,可登陆管理控制台,同时可以查看rabbitmq节点的相关信息(进程数,内存使用情况,磁盘使用情况等)
- Policymaker:策略制定者,可登陆管理控制台, 同时可以对policy进行管理。但无法查看节点的相关信息(上图红框标识的部分)
- Management:普通管理者, 仅可登陆管理控制台,无法看到节点信息,也无法对策略进行管理
- 其他:无法登陆管理控制台,通常就是普通的生产者和消费者。
创建虚拟主机
为了让各个用户可以互不干扰的工作,RabbitMQ添加了虚拟主机(Virtual Hosts)的概念。其实就是一个独立的访问路径,不同用户使用不同路径,各自有自己的队列、交换机,互相不会影响。
绑定虚拟主机和用户
3、RabbitMQ支持的消息模型
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-07wbxuy5-1620285282152)(C:\Users\Blanc\AppData\Roaming\Typora\typora-user-images\image-20210427160053810.png)]
添加端口访问
firewall-cmd --add-port=5672/tcp --permanent
firewall-cmd --reload
firewall-cmd --list-all
rabbitmq依赖
<dependency>
<groupId>com.rabbitmq</groupId>
<artifactId>amqp-client</artifactId>
<version>5.11.0</version>
</dependency>
第一种-基本模型(直连)
概述
P:生产者,要发送消息的程序
C:消费者,等待接收消息的程序
Queue:消息队列,图中红色部分。实质上是一个大的消息缓冲区。类似一个邮箱,可以缓存消息;生产者向其中投递消息,消费者从其中取出消息。遵循先进先出原则
开发工具类
public class RabbitmqUtils {
private static final ConnectionFactory connectionFactory;
static {
//创建连接mq的连接工厂对象,重量级对象,类加载时创建一次即可
connectionFactory = new ConnectionFactory();
//设置连接rabbitmq的主机
connectionFactory.setHost("192.168.232.134");
//设置端口号
connectionFactory.setPort(5672);
//设置连接的虚拟主机
connectionFactory.setVirtualHost("/ems");
//设置访问虚拟主机的用户名和密码
connectionFactory.setUsername("alice");
connectionFactory.setPassword("123");
}
//获取连接对象
public static Connection getConnection(){
try {
return connectionFactory.newConnection();
} catch (IOException | TimeoutException e) {
e.printStackTrace();
}
return null;
}
//关闭通道和连接
public static void close(Channel channel, Connection connection){
if(channel != null){
try {
channel.close();
} catch (IOException | TimeoutException e) {
e.printStackTrace();
}
}
if(connection != null){
try {
connection.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
开发生产者
public class Provider {
public static void main(String[] args) {
Connection connection = null;
Channel channel = null;
try {
//获取连接对象
connection = RabbitmqUtils.getConnection();
//获取通道
if (connection != null) {
channel = connection.createChannel();
//通道绑定对应消息队列
/*
* 参数1 queue:队列名称(不存在自动创建)
* 参数2 durable:用来定义队列特性是否需要持久化(为true该队列将在服务器重启后保留下来,持久化到硬盘中)
* 参数3 exclusive:是否独占队列(为true仅限此连接)
* 参数4 autoDelete:是否在消费完成后自动删除队列
* 参数5 arguments:队列的其他属性(构造参数)
* */
channel.queueDeclare("hello",false,false,false,null);
//发布消息
/*
* 参数1 exchange:要将消息发布到的交换机
* 餐数2 routingKey:路由键,指定队列
* 参数3 props:消息的其他属性
* 参数4 body:消息具体内容
* */
String message = "hello rabbitmq";
channel.basicPublish("","hello",null,message.getBytes());
}
} catch (IOException e) {
e.printStackTrace();
} finally {
System.out.println("生产者发布消息完成......");
RabbitmqUtils.close(channel,connection);
}
}
}
执行生产者测试方法,队列中成功添加进消息
开发消费者
public class Consumer {
public static void main(String[] args) {
Connection connection = null;
Channel channel = null;
try {
//获取连接对象
connection = RabbitmqUtils.getConnection();
//获取通道
if (connection != null) {
channel = connection.createChannel();
//通道绑定对应消息队列
/*
* 参数1 queue:队列名称(不存在自动创建)
* 参数2 durable:用来定义队列特性是否需要持久化(为true该队列将在服务器重启后保留下来)
* 参数3 exclusive:是否独占队列(为true仅限此连接)
* 参数4 autoDelete:是否在消费完成不再使用后自动删除队列
* 参数5 arguments:队列的其他属性(构造参数)
* */
channel.queueDeclare("hello",false,false,false,null);
//消费消息
DefaultConsumer defaultConsumer = new DefaultConsumer(channel){
//获取消息并且处理。此方法类似于事件监听,有消息时会被自动调用
@Override
public void handleDelivery(String consumerTag, Envelope envelope, AMQP.BasicProperties properties, byte[] body) throws IOException {
System.out.println("message:"+new String(body)); //body即消息体
}
};
/*
* 参数1 queue:队列名称
* 参数2 autoAck:开启消息的自动确认机制
* 参数3 Consumer callback:消费时的回调接口
* */
channel.basicConsume("hello",true, defaultConsumer);
}
} catch (IOException e) {
e.printStackTrace(