消息队列(MQ)
本质上就是一个队列,一个先进先出的队列,队列中存放的内容是message(消息),是一种跨进程的通信机制,用于上下游传递消息,
为什么使用MQ:
削峰填谷:
MQ可以很好的做一个缓冲机制,例如在一个系统中有A和B两个应用,A是接收用户的请求的,然后A调用B进行处理.
这时有可能A收到的请求会达到一个峰值,由于A和B是直接调用的关系,所以B也是达到一个峰值,如果A 1s收到了3w次请求,那么B也是1s 收到3w次请求,这是对B的压力是非常大的,有可能会导致B直接宕机.
这时就可以使用消息队列来达到削峰填谷的效果.
我们可以在A和B之间添加一个消息队列,让A接收到请求之后,不在直接调用B了,而是将请求全部作为消息放入到消息队列中,而B则是直接在消息队列中获取消息(这个消息就是A放入的用户的请求),
这时让B按照一定的速率进行获取消息,
这样做之后,尽管出现1s 3万的请求,这时我们的A会将所有的请求全部放入消息队列中,然后B会将消息队列中的消息按照一定的速率进行获取,这个时候B就不会出现同一时刻有大量的请求同时触发了.(这就是削峰)
当A接收的请求次数降低之后,这时就会达到一个波谷,此时B任然还可以按照一定的速率进行获取消息队列中的消息,(也就是之前挤压的数据),(填谷)
生产者消费者模型
MQ的四大核心概念:
生产者:产生数据发送消息的程序
交换机:是消息队列中一个部件,可以接收来自生产者的消息,也可以将消息推送到消息队列中,交换机必须确切的知道要如何处理接收到的消息,是将消息推送到特定的队列,还是将消息推送到所有队列中,获取将消息丢弃.
队列:是消息队列内部的一种数据结构,尽管消息是通过消息队列流经应用程序的,但是消息只能存储在队列中,队列本质是一个大的消息缓冲区,许多生产者可以将消息发送到一个队列,许多消费者可以从一个队列中获取消息.
消费者: 消费者与接受者具有相同的含义,消费者大多数是一个等待接收消息的应用程序.
消息队列的工作原理
Broker(中间人) 接收和分发消息的应用,消息队列服务器就是Broker.
virtual host : 当不同的用户在使用同一个消息队列时,可以划分出多个vhost,每个用户在自己的vhost中创建自己的exchange和queue.
connection: 连接 生成者(Producer)和消费者(Consumer)与消息队列之间的TCP连接.
channel: 如果每次访问消息队列都要建立一个TCP连接,那么当消息很多的时候,就会建立很多TCP连接,这时建立TCP连接的开销是非常大的,效率也是很低的.channel是在connection内部建立的逻辑连接,而且channel之间是完全隔离的.
exchange: 交换机,这是消息到达消息队列的第一站,消息队列会根据分发规则,匹配查询表中的routing key 将消息分发的对应的queue中去.
queue: 消息在这里等待被Consumer(消费者)取走.
binding: exchange和queue之间的逻辑连接.,binding可以包含routingKey,Binding信息被保存到exchange中的查询表中,用户消息的分发.
安装RabbitMQ
环境:CentOS7
注意:需要下载RabbitMQ的依赖以及RabbitMQ的安装包和erlang,同时在下载的时候需要注意版本问题.
这里我已经准备好了对应的安装包,需要可以私信我,我发给大家.
将我们准备好的安装包传输到CentOS上之后,就可以进行安装了.
安装erlang
rpm -ivh erlang-23.3.4.11-1.el7.x86_64.rpm
安装erlang 依赖
rpm -ivh openssl-libs-1.0.2k-25.el7_9.x86_64.rpm
安装 socat 依赖
yum install socat -y
安装rabbitMQ
rpm -ivh rabbitmq-server-3.8.8-1.el8.noarch.rpm
启动服务
systemctl start rabbitmq-server
查看服务状态
systemctl status rabbitmq-server
开机自启动
systemctl enable rabbitmq-server
停止服务
systemctl stop rabbitmq-server
重启服务
systemctl restart rabbitmq-server
安装 Web 端的客户端软件
rabbitmq-plugins enable rabbitmq_management 需要打开默认端口
防火墙打开15672端口
网页通过http://IP:端口进行访问 默认账号密码guest
添加一个远程登录的用户
创建账号和密码
rabbitmqctl add_user DGZ Dgz@#151
设置用户角色
rabbitmqctl set_user_tags DGZadministrator
为用户添加资源权限,添加配置、写、读权限
rabbitmqctl set_permissions -p "/" DGZ".*" ".*" ".*"
修改密码
rabbitmqctl change_ password 用户名 新密码
删除用户
rabbitmqctl delete_user 用户名
查看用户清单
rabbitmqctl DGZ
RabbitMQ的入门案例
创建的一个maven工程或者springBoot项目都可以.
引入依赖
XML
<!--rabbitmq 依赖客户端-->
<dependency>
<groupId>com.rabbitmq</groupId>
<artifactId>amqp-client</artifactId>
<version>5.8.0</version>
</dependency>
<!--操作文件流的一个依赖-->
<dependency>
<groupId>commons-io</groupId>
<artifactId>commons-io</artifactId>
<version>2.6</version>
</dependency>
创建生产者
java
public class Producer {
//队列名称
public static final String QueueName = "hello";
public static void main(String[] args) throws IOException, TimeoutException {
ConnectionFactory factory = new ConnectionFactory(); //创建连接工厂
factory.setHost("127.0.0.1"); //这里的IP地址就是大家centos的IP地址
factory.setUsername("DGZ"); //用户名
factory.setPassword("Dgz@#151"); // 密码
Connection connection = factory.newConnection(); //通过连接工厂创建一个连接
Channel channel = connection.createChannel(); //获取信道
/**
* 生产一个对列
* 1.对列名称
* 2.对列里面的消息是否持久化,默认情况下,消息存储在内存中
* 3.该队列是否只供一个消费者进行消费,是否进行消息共享,true可以多个消费者消费 false:只能一个消费者消费
* 4.是否自动删除,最后一个消费者端开链接以后,该队列是否自动删除,true表示自动删除
* 5.其他参数
*/
channel.queueDeclare(QueueName,false,false,false,null);
String message = "hello world";
/**
* 发送一个消息
* 1.发送到哪个交换机
* 2.路由的key值是哪个本次是队列的名称
* 3.其他参数信息
* 4.发送消息的消息体
*/
channel.basicPublish("",QueueName,null,message.getBytes());
System.out.println("消息发送完毕");
}
}
创建消费者
java
public class Consumer {
public static final String QueueName = "hello";
public static void main(String[] args) throws IOException, TimeoutException {
ConnectionFactory factory = new ConnectionFactory();
factory.setHost("127.0.0.1");
factory.setUsername("DGZ");
factory.setPassword("Dgz@#151");
Connection connection = factory.newConnection();
Channel channel = connection.createChannel();
//声明接收消息
DeliverCallback deliverCallback = (consumerTag,message) -> {
System.out.println(new String(message.getBody()));
};
//取消消息时的回调
CancelCallback cancelCallback = consumerTag ->{
System.out.println("消息消费被中断");
};
/**
* 消费者消费消息
* 1.消费哪个队列
* 2.消费成功之后是否要自动应答true:代表自动应答false:代表手动应答
* 3.消费者未成功消费的回调
* 4.消费者取消消费的回调
*/
channel.basicConsume(QueueName,true,deliverCallback,cancelCallback);
}
}
此时我们先启动生产者代码,然后在启动消费者代码.这样一个简单的RabbitMQ的入门案例就完成了.
WorkQueues(工作队列)
WorkQueues是工作队列,又称任务队列,主要思想就是避免立即执行资源密集型任务,而不得不等待它完成.
相反我们安排任务在之后执行,我们把任务封装为消息并将其发送到队列,在后台运行的工作进程将弹出任务并最终执行作业,当有多个工作线程时,这些工作线程将一起处理这些任务.
我们将消费者main方法设置为开启对个实例之后,然后生产者发送消息,此时就会发送消费者是以轮询消费的方式消费消息的.
生产者:
消费线程1:
消费线程2: