RabbitMQ基础入门
1、什么MQ?
MQ全称 Message Queue(消息队列),是在消息的传输过程中保存消息的容器。多用于分布式系统之间进行通信。
2、MQ的优缺点
部分图片来源:Wanght6
优点
1、 应用解耦:提高系统容错性和可维护性
2、异步提速:提升用户体验和系统吞吐量
外卖例子
使用RabbirMQ
3、削峰填谷:提高系统稳定性
假设我们有一个应用,平时访问量是每秒100请求,我们用一台服务器即可轻松应对
而在高峰期,访问量瞬间翻了十倍,达到每秒1000次请求,那么单台服务器肯定无法应对,这时我们可以考虑增加到10台服务器,来分散访问压力,这样就有点浪费资源了
这种情况,我们就可以使用RabbitMQ来进行流量削峰
这是消息队列服务器非常典型的应用场景
缺点
- 系统可用性降低
系统引入的外部依赖越多,系统稳定性越差。一旦 MQ 宕机,就会对业务造成影响。 - 系统复杂度提高
MQ 的加入大大增加了系统的复杂度,以前系统间是同步的远程调用,现在是通过 MQ 进行异步调用。
3、常见的MQ有哪些?
4、rabbitMQ的结构以及每个组件的作用
-
Broker
接收和分发消息的应用,RabbitMQ Server就是 Message Broker -
Virtual host
出于多租户和安全因素设计的,把 AMQP 的基本组件划分到一个虚拟的分组中,类似于网络中的 namespace 概念。当多个不同的用户使用同一个 RabbitMQ server 提供的服务时,可以划分出多个vhost,每个用户在自己的 vhost 创建 exchange/queue 等 -
Connection
publisher/consumer 和 broker 之间的 TCP 连接(生产者和消费者之间的连接) -
Channel
如果每一次访问 RabbitMQ 都建立一个 Connection,在消息量大的时候建立 TCP Connection的开销将是巨大的,效率也较低。Channel 是在 connection 内部建立的逻辑连接,如果应用程序支持多线程,通常每个thread创建单独的 channel 进行通讯,AMQP method 包含了channel id 帮助客户端和message broker 识别 channel,所以 channel 之间是完全隔离的。Channel 作为轻量级的 Connection 极大减少了操作系统建立 TCP connection 的开销 -
Exchange
message 到达 broker 的第一站,根据分发规则,匹配查询表中的 routing key,分发消息到queue 中去。常用的类型有:direct (point-to-point), topic (publish-subscribe) and fanout (multicast) -
Queue
消息最终被送到这里等待 consumer 取走 -
Binding
exchange 和 queue 之间的虚拟连接,binding 中可以包含 routing key。Binding 信息被保存到 exchange 中的查询表中,用于 message 的分发依据
6、RabbitMQ的模式
RabbitMQ常用的工作模式有:简单队列模式、工作队列模式、发布订阅模式、路由模式、主题模式等五种
(1)简单模式
模型图:只包含一个生产者以及一个消费者,生产者Producer将消息发送到队列中,消费者Consumer从该队列接收消息。(单生产单消费)
特点:只有生产者、消费者和队列组成。
P [product]: 生产者 发生消息的
红色[queue]: 队列。 存储消息的
C [consumer]: 消费者 消费消息
- 代码操作
1、首先创建一个springboot项目,然后把项目中的src目录删掉!!
2、创建生产者子项目
点击项目名右键新建–》Module项目–》选择Maven项目–》Next–》修改生产者项目名为product–》finish
同理创建消费者consumer子项目
父类中的pom.xml文件
3、打开虚拟机,启动rabbitMQ服务!!
生产者代码(在product项目中创建包并创建类书写代码)
public class Product {
public static void main(String[] args)throws Exception {
//创建连接工厂 --配置连接信息
ConnectionFactory factory=new ConnectionFactory();
factory.setHost("192.168.31.35");
//创建连接对象Connection
Connection connection=factory.newConnection();
//创建信道
Channel channel = connection.createChannel();
//创建队列
/**
* String queue, 队列的名称
* boolean durable, 是否该队列持久化 rabbitMQ服务重启后该存放是否存在。
* boolean exclusive, 是否独占 false
* boolean autoDelete, 是否自动删除 如果长时间没有发生消息 则自动删除
* Map<String, Object> arguments 额外参数 先给null
*/
channel.queueDeclare("fyx01",true,false,false,null);
//发生消息
/**
* String exchange: 交换机的名称 如果没有则使用“” 它回自动采用默认
* String routingKey, 路由key 如果没有交换机的绑定 使用队列的名称
* BasicProperties props, 消息的一些额外配置 目前先不加 null
* byte[] body 消息的内容
*/
String msg="超人!!!";
channel.basicPublish("","fyx01",null,msg.getBytes());
}
}
消费者代码(在consumer项目中创建包并创建类书写代码)
public class Consumer {
public static void main(String[] args) throws Exception{
//创建连接工厂 --配置连接信息
ConnectionFactory factory=new ConnectionFactory();
factory.setHost("192.168.31.35");
//创建连接对象Connection
Connection connection=factory.newConnection();
//创建信道
Channel channel = connection.createChannel();
//接受消息
/**
* (String queue, 队列的名称
* boolean autoAck, 是否自动确认
* Consumer callback: 回调方法 当队列中存在信息后 会自动触发回调函数。
*/
DefaultConsumer callback=new DefaultConsumer(channel){
@Override
public void handleDelivery(String consumerTag, Envelope envelope, AMQP.BasicProperties properties, byte[] body) throws IOException {
//body 接受的信息
System.out.println("消息的内容:"+new String(body));
}
};
channel.basicConsume("fyx01",true,callback);
}
}
4、测试
(2)工作者模式
模型图:多个消费者绑定到同一个队列上,一条消息只能被一个消费者进行消费。工作队列有轮训分发和公平分发两种模式。
- 特点:
- 一个生产者
- 由多个消费。
- 统一个队列。
- 这些消费者之间存在竞争关系。
- 用处:
比如批量处理上。rabbitMQ里面积压了大量的消息。
生产者代码
public class Product {
public static void main(String[] args)throws Exception {
//创建连接工厂 --配置连接信息
ConnectionFactory factory=new ConnectionFactory();
factory.setHost("192.168.31.35");
//创建连接对象Connection
Connection connection=factory.newConnection();
//创建信道
Channel channel = connection.createChannel();
//创建队列
/**
* String queue, 队列的名称
* boolean durable, 是否该队列持久化 rabbitMQ服务重启后该存放是否存在。
* boolean exclusive, 是否独占 false
* boolean autoDelete, 是否自动删除 如果长时间没有发生消息 则自动删除
* Map<String, Object> arguments 额外参数 先给null
*/
channel.queueDeclare("fyx01",true,false,false,null);
//发生消息
/**
* String exchange: 交换机的名称 如果没有则使用“” 它回自动采用默认
* String routingKey, 路由key 如果没有交换机的绑定 使用队列的名称
* BasicProperties props, 消息的一些额外配置 目前先不加 null
* byte[] body 消息的内容
*/
for (int i=0;i<10;i++){
String msg="超人!!!"; channel.basicPublish("","fyx01",null,msg.getBytes());
}
//生产者关闭资源
channel.close();
connection.close();
}
}
消费者01代码
public class Consumer1 {
public static void main(String[] args) {
//创建连接工厂 --配置连接信息
ConnectionFactory factory=new ConnectionFactory();
factory.setHost("192.168.31.35");
//创建连接对象Connection
Connection connection= null;
try {
connection = factory.newConnection();
//创建信道
Channel channel = connection.createChannel();
//接受消息
/**
* (String queue, 队列的名称
* boolean autoAck, 是否自动确认
* Consumer callback: 回调方法 当队列中存在信息后 会自动触发回调函数。
*/
DefaultConsumer callback = new DefaultConsumer(channel){
@Override
public void handleDelivery(String consumerTag, Envelope envelope, AMQP.BasicProperties properties, byte[] body) throws IOException {
try {
Thread.sleep(1000L);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("消费者01内容:"+new String(body));
}
};
channel.basicConsume("fyx01_direct01",true,callback);
} catch (IOException e) {
e.printStackTrace();
} catch (TimeoutException e) {
e.printStackTrace();
}
}
}
消费者02代码
public class Consumer2 {
public static void main(String[] args) {
//创建连接工厂 --配置连接信息
ConnectionFactory factory=new ConnectionFactory();
factory.setHost("192.168.31.35");
//创建连接对象Connection
Connection connection= null;
try {
connection = factory.newConnection();
//创建信道
Channel channel = connection.createChannel();
//接受消息
/**
* (String queue, 队列的名称
* boolean autoAck, 是否自动确认
* Consumer callback: 回调方法 当队列中存在信息后 会自动触发回调函数。
*/
DefaultConsumer callback = new DefaultConsumer(channel){
@Override
public void handleDelivery(String consumerTag, Envelope envelope, AMQP.BasicProperties properties, byte[] body) throws IOException {
try {
Thread.sleep(1000L);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("消费者02的内容:"+new String(body));
}
};
channel.basicConsume("fyx02_direct02",true,callback);
} catch (IOException e) {
e.printStackTrace();
} catch (TimeoutException e) {
e.printStackTrace();
}
}
}
(3)发布-订阅模式
模型图:生产者将消息发送到交换器,然后交换器绑定到多个队列,监听该队列的所有消费者消费消息。
- 特点:
- 一个生产者
- 多个队列。
- 交换机 转发消息。
生产者代码
public class Product {
public static void main(String[] args) {
//创建创建连接工厂 --配置连接信息
ConnectionFactory factory = new ConnectionFactory();
//创建连接虚拟机中的IP
factory.setHost("192.168.31.35");
try {
//创建连接对象
Connection connection = factory.newConnection();
//创建信道
Channel channel = connection.createChannel();
//创建队列
/**
* String queue:队列名称(自己起名)
* boolean durable:是否该队列化 rabbitMQ服务重启后该存放是否存在
* boolean exclusive, 是否独占 false
* boolean autoDelete, 是否自动删除 如果长时间没有发生消息 则自动删除
* Map<String, Object> arguments 额外参数 先给null
*/
channel.queueDeclare("fyx01",true,false,false,null);
channel.queueDeclare("fyx02",true,false,false,null);
//创建交换机
/**
* String exchange,交换机的名称
* BuiltinExchangeType type, 交换机的类型
* boolean durable:是否持久化
*/
channel.exchangeDeclare("aaa", BuiltinExchangeType.FANOUT,true);
channel.queueBind("fyx01","aaa","");
channel.queueBind("fyx02","aaa","");
//发生信息
/**
* (String queue, 队列的名称
* boolean autoAck, 是否自动确认
* Consumer callback: 回调方法 当队列中存在信息后 会自动触发回调函数。
*/
for (int i=0;i<10;i++){
String msg = "xx大战蜘蛛侠!!!"+i;
//channel.basicPublish("aaa","fyx01",null,msg.getBytes());
channel.basicPublish("aaa", "", null, msg.getBytes());
}
//生产者关闭资源
channel.close();
connection.close();
} catch (IOException e) {
e.printStackTrace();
} catch (TimeoutException e) {
e.printStackTrace();
}
}
}
消费者01代码
public class Consumer1 {
public static void main(String[] args) {
//创建连接工厂 --配置连接信息
ConnectionFactory factory=new ConnectionFactory();
factory.setHost("192.168.31.35");
//创建连接对象Connection
Connection connection= null;
try {
connection = factory.newConnection();
//创建信道
Channel channel = connection.createChannel();
//接受消息
/**
* (String queue, 队列的名称
* boolean autoAck, 是否自动确认
* Consumer callback: 回调方法 当队列中存在信息后 会自动触发回调函数。
*/
DefaultConsumer callback = new DefaultConsumer(channel){
@Override
public void handleDelivery(String consumerTag, Envelope envelope, AMQP.BasicProperties properties, byte[] body) throws IOException {
try {
Thread.sleep(1000L);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("消费者01内容:"+new String(body));
}
};
channel.basicConsume("fyx01",true,callback);
} catch (IOException e) {
e.printStackTrace();
} catch (TimeoutException e) {
e.printStackTrace();
}
}
}
消费者02代码
public class Consumer2 {
public static void main(String[] args) {
//创建连接工厂 --配置连接信息
ConnectionFactory factory=new ConnectionFactory();
factory.setHost("192.168.31.35");
//创建连接对象Connection
Connection connection= null;
try {
connection = factory.newConnection();
//创建信道
Channel channel = connection.createChannel();
//接受消息
/**
* (String queue, 队列的名称
* boolean autoAck, 是否自动确认
* Consumer callback: 回调方法 当队列中存在信息后 会自动触发回调函数。
*/
DefaultConsumer callback = new DefaultConsumer(channel){
@Override
public void handleDelivery(String consumerTag, Envelope envelope, AMQP.BasicProperties properties, byte[] body) throws IOException {
try {
Thread.sleep(1000L);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("消费者02的内容:"+new String(body));
}
};
channel.basicConsume("fyx02",true,callback);
} catch (IOException e) {
e.printStackTrace();
} catch (TimeoutException e) {
e.printStackTrace();
}
}
}
- 测试
(4)路由模式
模型图:生产者将消息发送到direct交换器,它会把消息路由到那些binding key与routing key完全匹配的Queue中,这样就能实现消费者有选择性地去消费消息。
- 特点
- 一个生产者
- 多个消费者
- 多个队列。
- 交换机 转发消息。
- routekey:路由key 只要routekey匹配的消息可以到达对应队列。
生产者代码
public class Product {
public static void main(String[] args) {
//创建创建连接工厂 --配置连接信息
ConnectionFactory factory = new ConnectionFactory();
//创建连接虚拟机中的IP
factory.setHost("192.168.31.35");
try {
//创建连接对象
Connection connection = factory.newConnection();
//创建信道
Channel channel = connection.createChannel();
//创建队列
/**
* String queue:队列名称(自己起名)
* boolean durable:是否该队列化 rabbitMQ服务重启后该存放是否存在
* boolean exclusive, 是否独占 false
* boolean autoDelete, 是否自动删除 如果长时间没有发生消息 则自动删除
* Map<String, Object> arguments 额外参数 先给null
*/
channel.queueDeclare("fyx01_direct01",true,false,false,null);
channel.queueDeclare("fyx02_direct02",true,false,false,null);
/**
* String exchange,交换机的名称
* BuiltinExchangeType type, 交换机的类型
* boolean durable:是否持久化
*/
channel.exchangeDeclare("aaa_direct", BuiltinExchangeType.DIRECT,true);
channel.queueBind("fyx01_direct01","aaa_direct","error");
channel.queueBind("fyx02_direct02","aaa_direct","info");
channel.queueBind("fyx02_direct02","aaa_direct","error");
channel.queueBind("fyx02_direct02","aaa_direct","warning");
//发生信息
/**
* (String queue, 队列的名称
* boolean autoAck, 是否自动确认
* Consumer callback: 回调方法 当队列中存在信息后 会自动触发回调函数。
*/
String a1 = "金刚吃了吗???!!!";
channel.basicPublish("aaa_direct","warning",null,a1.getBytes());
for (int i=0;i<10;i++){
String msg = "金刚大战哥斯拉!!!"+i;
channel.basicPublish("aaa_direct","error",null,msg.getBytes());
}
//生产者关闭资源
channel.close();
connection.close();
} catch (IOException e) {
e.printStackTrace();
} catch (TimeoutException e) {
e.printStackTrace();
}
}
}
消费者01的代码
public class Consumer1 {
public static void main(String[] args) {
//创建连接工厂 --配置连接信息
ConnectionFactory factory=new ConnectionFactory();
factory.setHost("192.168.31.35");
//创建连接对象Connection
Connection connection= null;
try {
connection = factory.newConnection();
//创建信道
Channel channel = connection.createChannel();
//接受消息
/**
* (String queue, 队列的名称
* boolean autoAck, 是否自动确认
* Consumer callback: 回调方法 当队列中存在信息后 会自动触发回调函数。
*/
DefaultConsumer callback = new DefaultConsumer(channel){
@Override
public void handleDelivery(String consumerTag, Envelope envelope, AMQP.BasicProperties properties, byte[] body) throws IOException {
try {
Thread.sleep(1000L);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("消费者01内容:"+new String(body));
}
};
channel.basicConsume("fyx01_direct01",true,callback);
} catch (IOException e) {
e.printStackTrace();
} catch (TimeoutException e) {
e.printStackTrace();
}
}
}
消费者02的代码
public class Consumer2 {
public static void main(String[] args) {
//创建连接工厂 --配置连接信息
ConnectionFactory factory=new ConnectionFactory();
factory.setHost("192.168.31.35");
//创建连接对象Connection
Connection connection= null;
try {
connection = factory.newConnection();
//创建信道
Channel channel = connection.createChannel();
//接受消息
/**
* (String queue, 队列的名称
* boolean autoAck, 是否自动确认
* Consumer callback: 回调方法 当队列中存在信息后 会自动触发回调函数。
*/
DefaultConsumer callback = new DefaultConsumer(channel){
@Override
public void handleDelivery(String consumerTag, Envelope envelope, AMQP.BasicProperties properties, byte[] body) throws IOException {
try {
Thread.sleep(1000L);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("消费者02的内容:"+new String(body));
}
};
channel.basicConsume("fyx02_direct02",true,callback);
} catch (IOException e) {
e.printStackTrace();
} catch (TimeoutException e) {
e.printStackTrace();
}
}
}
- 测试结果
(5)topic主体模式
模型图:类似于正则表达式匹配的一种模式。主要使用#、*进行匹配。
#1. 绑定按照通配符的模式。
*: 统配一个单词。
#: 统配n个单词
#例如:
hello.orange.rabbit
lazy.orange
生产者代码
public class Product {
public static void main(String[] args) {
//创建创建连接工厂 --配置连接信息
ConnectionFactory factory = new ConnectionFactory();
//创建连接虚拟机中的IP
factory.setHost("192.168.31.35");
try {
//创建连接对象
Connection connection = factory.newConnection();
//创建信道
Channel channel = connection.createChannel();
//创建队列
/**
* String queue:队列名称(自己起名)
* boolean durable:是否该队列化 rabbitMQ服务重启后该存放是否存在
* boolean exclusive, 是否独占 false
* boolean autoDelete, 是否自动删除 如果长时间没有发生消息 则自动删除
* Map<String, Object> arguments 额外参数 先给null
*/
channel.queueDeclare("fyx01_topic01",true,false,false,null);
channel.queueDeclare("fyx02_topic02",true,false,false,null);
/**
* String exchange,交换机的名称
* BuiltinExchangeType type, 交换机的类型
* boolean durable:是否持久化
*/
channel.exchangeDeclare("aaa_topic", BuiltinExchangeType.TOPIC,true);
channel.queueBind("fyx01_topic01","aaa_topic","*.orange.*");
channel.queueBind("fyx02_topic02","aaa_topic","*.*.rabbit");
channel.queueBind("fyx02_topic02","aaa_topic","lazy.#");
//发生信息
/**
* (String queue, 队列的名称
* boolean autoAck, 是否自动确认
* Consumer callback: 回调方法 当队列中存在信息后 会自动触发回调函数。
*/
for (int i=0;i<10;i++){
String msg = "大猩猩大战美眉"+i;
channel.basicPublish("aaa_topic","lazy.orange",null,msg.getBytes());
}
//生产者关闭资源
channel.close();
connection.close();
} catch (IOException e) {
e.printStackTrace();
} catch (TimeoutException e) {
e.printStackTrace();
}
}
}
消费者01的代码
public class Consumer1 {
public static void main(String[] args) {
//创建连接工厂 --配置连接信息
ConnectionFactory factory=new ConnectionFactory();
factory.setHost("192.168.31.35");
//创建连接对象Connection
Connection connection= null;
try {
connection = factory.newConnection();
//创建信道
Channel channel = connection.createChannel();
//接受消息
/**
* (String queue, 队列的名称
* boolean autoAck, 是否自动确认
* Consumer callback: 回调方法 当队列中存在信息后 会自动触发回调函数。
*/
DefaultConsumer callback = new DefaultConsumer(channel){
@Override
public void handleDelivery(String consumerTag, Envelope envelope, AMQP.BasicProperties properties, byte[] body) throws IOException {
try {
Thread.sleep(1000L);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("消费者01内容:"+new String(body));
}
};
channel.basicConsume("fyx01_topic01",true,callback);
} catch (IOException e) {
e.printStackTrace();
} catch (TimeoutException e) {
e.printStackTrace();
}
}
}
消费者02的代码
public class Consumer2 {
public static void main(String[] args) {
//创建连接工厂 --配置连接信息
ConnectionFactory factory=new ConnectionFactory();
factory.setHost("192.168.31.35");
//创建连接对象Connection
Connection connection= null;
try {
connection = factory.newConnection();
//创建信道
Channel channel = connection.createChannel();
//接受消息
/**
* (String queue, 队列的名称
* boolean autoAck, 是否自动确认
* Consumer callback: 回调方法 当队列中存在信息后 会自动触发回调函数。
*/
DefaultConsumer callback = new DefaultConsumer(channel){
@Override
public void handleDelivery(String consumerTag, Envelope envelope, AMQP.BasicProperties properties, byte[] body) throws IOException {
try {
Thread.sleep(1000L);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("消费者02的内容:"+new String(body));
}
};
channel.basicConsume("fyx02_topic02",true,callback);
} catch (IOException e) {
e.printStackTrace();
} catch (TimeoutException e) {
e.printStackTrace();
}
}
}
- 测试结果