1. MQ 的基本概念
1.1 MQ概述
小结
- MQ,消息队列,存储消息的中间件
- 分布式系统通信两种方式:直接远程调用 和 借助第三方 完成间接通信
- 发送方称为生产者,接收方称为消费者
1.2 MQ 的优势和劣势
1.3 MQ 的优势
1. 应用解耦
远程调用产生的问题:耦合
1.容错性:如果产生异常一条链路挂了,导致订单系统也可能出现问题,用户就会得到下单失败
2.可维护性:增删X系统都要修改订单系统
mq解决的问题
1.容错性提高,隔离,链路挂了可能过几分钟就会修复,不会影响到订单系统
2.如果需要增加x系统,在mq里面拿出信息消费就行,订单系统一行都不需要修改
2.异步提速
3.削峰填谷
MQ 的优势 小结
- 应用解耦:提高系统容错性和可维护性
- 异步提速:提升用户体验和系统吞吐量
- 削峰填谷:提高系统稳定性
1.4 MQ 的劣势
1.系统的可用性
系统引入的外部依赖越多,系统的稳定性就越差,我们一开始只需要维护系统A和B,现在还有维护mq中间件。
2.系统的复杂度
3.系统的一致性
总结
1.5 常见的 MQ 产品
1.6 RabbitMQ 简介
小结
2. RabbitMQ 的安装和配置
下载最新版本的镜像:
docker pull rabbitmq
2、创建并运行 RabbitMQ
容器
docker run -d -p 15672:15672 -p 5672:5672 \
-e RABBITMQ_DEFAULT_VHOST=my_vhost \
-e RABBITMQ_DEFAULT_USER=admin \
-e RABBITMQ_DEFAULT_PASS=admin \
--hostname myRabbit \
--name rabbitmq \
rabbitmq
设置 docker 启动的时候自动启动(可选):
docker update rabbitmq --restart=always
3、启动 rabbitmq_management
方法一:
docker exec -it rabbitmq /bin/bash
---------------------------------
user@7b295c46c99d /: rabbitmq-plugins enable rabbitmq_management
方法二::
docker exec -it rabbitmq rabbitmq-plugins enable rabbitmq_management
4、访问 RabbitMQ 后台管理
浏览器输入地址:http://ip:15672 即可访问后台管理页面,这里的 ip 为运行 RabbitMQ 所在的服务器的 IP 地址;
默认的用户名和密码都是 guest(如果没有在容器创建的时候指定用户名密码);
3. RabbitMQ 快速入门
1.简单模式
- 创建工程(生成者、消费者)
- 分别添加依赖
- 编写生产者发送消息
- 编写消费者接收消息
1.创建工程(生成者、消费者)
2.分别添加依赖
<dependencies>
<dependency>
<groupId>com.rabbitmq</groupId>
<artifactId>amqp-client</artifactId>
<version>5.14.2</version>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<version>3.8.0</version>
<configuration>
<source>1.8</source>
<target>1.8</target>
</configuration>
</plugin>
</plugins>
</build>
3.编写生产者发送消息
- 创建连接工厂
- 设置参数
- 创建连接connection
- 创建channel
- 创建队列queue
- 发送消息
public class Producer_helloWorld {
public static void main(String[] args) throws IOException, TimeoutException {
//1. 创建连接工厂
ConnectionFactory factory = new ConnectionFactory();
//2.设置参数
factory.setHost("8.130.108.134");//ip
factory.setPort(5672);//端口
factory.setVirtualHost("/itcast");//虚拟机
factory.setUsername("heima");//用户名
factory.setPassword("heima");//密码
//3.创建连接connection
Connection connection = factory.newConnection();
//4.创建channel
Channel channel = connection.createChannel();
//5.创建队列queue
/**
* queueDeclare(String queue, boolean durable, boolean exclusive,
* boolean autoDelete, Map<String, Object> arguments)
* 参数:
* 1.queue:队列名称
* 2.durable:是否持久化
* 3.exclusive
* *是否独占。只能有一个消费者监听这个队列
* *当connection关闭时,是否删除队列
* 4.autoDelete:是否自动删除,当没有consumer时,自动删除掉
* 5.arguments:参数信息
*/
//如果没有一个叫hello_world的队列,则会创建改队列,如果有则不会创建
channel.queueDeclare("hello_world",true,false,false,null);
/**
* basicPublish(String exchange, String routingKey, BasicProperties props, byte[] body)
* 1.exchange:交换机名称。简单模式下交换机会使用默认的“ ”
* 2.routingKey:路由名称
* 3.props:配置信息
* 4.body:发送的信息
*/
//6.发送消息
channel.basicPublish("","hello_world",null,"hello rabbitmq".getBytes());
//7.释放资源
channel.close();
connection.close();
}
4.编写消费者消费消息
- 创建连接工厂
- 设置参数
- 创建连接connection
- 创建channel
- 创建队列queue
- 发送消息
public static void main(String[] args) throws IOException, TimeoutException {
//1.创建连接工厂
ConnectionFactory factory = new ConnectionFactory();
//2.设置参数
factory.setHost("8.130.108.134");//ip
factory.setPort(5672);//端口
factory.setVirtualHost("/itcast");//虚拟机
factory.setUsername("heima");//用户名
factory.setPassword("heima");//密码
//3.创建连接
Connection connection = factory.newConnection();
//4.创建channel
Channel channel = connection.createChannel();
//5.创建队列queue
/**
* queueDeclare(String queue, boolean durable, boolean exclusive,
* boolean autoDelete, Map<String, Object> arguments)
* 参数:
* 1.queue:队列名称
* 2.durable:是否持久化
* 3.exclusive
* *是否独占。只能有一个消费者监听这个队列
* *当connection关闭时,是否删除队列
* 4.autoDelete:是否自动删除,当没有consumer时,自动删除掉
* 5.arguments:参数信息
*/
//如果没有一个叫hello_world的队列,则会创建改队列,如果有则不会创建
channel.queueDeclare("hello_world",true,false,false,null);
/**
*
* public String basicConsume(String queue, boolean autoAck, Consumer callback)
* 1.queue:队列名称
* 2.autoAck:是否自动确认
* 3.callback:回调对象
*/
//6.发送消息
Consumer consumer = new DefaultConsumer(channel){
/**
*这是一个回调方法,收到消息后,自动执行该方法
*1.consumerTag:标记
* 2.envelope:获取一些信息,交换机,路由
* 3.properties:配置信息
* 4.body:数据
*/
@Override
public void handleDelivery(String consumerTag, Envelope envelope, AMQP.BasicProperties properties, byte[] body) throws IOException {
System.out.println("consumerTag:"+consumerTag);
System.out.println("Envelope:"+envelope.getExchange());
System.out.println("RoutingKey:"+envelope.getRoutingKey());
System.out.println("properties:"+properties);
System.out.println("body:"+new String(body));
}
};
channel.basicConsume("hello_world",true,consumer);
//7.消费者不用关闭资源
}
小结
4. RabbitMQ 的工作模式
4.1 Work queues 工作队列模式
1. 模式说明
- Work Queues:与入门程序的简单模式相比,多了一个或一些消费端,多个消费端共同消费同一个队列中的消息。
- 应用场景:对于任务过重或任务较多情况使用工作队列可以提高任务处理的速度。
1.生产者
Producer_WorkQueues
public class Producer_WorkQueues {
public static void main(String[] args) throws IOException, TimeoutException {
//1. 创建连接工厂
ConnectionFactory factory = new ConnectionFactory();
//2.设置参数
factory.setHost("8.130.108.134");//ip
factory.setPort(5672);//端口
factory.setVirtualHost("/itcast");//虚拟机
factory.setUsername("heima");//用户名
factory.setPassword("heima");//密码
//3.创建连接connection
Connection connection = factory.newConnection();
//4.创建channel
Channel channel = connection.createChannel();
//5.创建队列queue
/**
* queueDeclare(String queue, boolean durable, boolean exclusive,
* boolean autoDelete, Map<String, Object> arguments)
* 参数:
* 1.queue:队列名称
* 2.durable:是否持久化
* 3.exclusive
* *是否独占。只能有一个消费者监听这个队列
* *当connection关闭时,是否删除队列
* 4.autoDelete:是否自动删除,当没有consumer时,自动删除掉
* 5.arguments:参数信息
*/
//如果没有一个叫hello_world的队列,则会创建改队列,如果有则不会创建
channel.queueDeclare("work_queues",true,false,false,null);
/**
* basicPublish(String exchange, String routingKey, BasicProperties props, byte[] body)
* 1.exchange:交换机名称。简单模式下交换机会使用默认的“ ”
* 2.routingKey:路由名称
* 3.props:配置信息
* 4.body:发送的信息
*/
for (int i = 0; i < 10; i++) {
//6.发送消息
channel.basicPublish("","work_queues",null,("hello work_queues"+i).getBytes());
}
//7.释放资源
channel.close();
connection.close();
}
}
2.消费者
Consumer_WorkQueues1
public class Consumer_WorkQueues1 {
public static void main(String[] args) throws IOException, TimeoutException {
//1.创建连接工厂
ConnectionFactory factory = new ConnectionFactory();
//2.设置参数
factory.setHost("8.130.108.134");//ip
factory.setPort(5672);//端口
factory.setVirtualHost("/itcast");//虚拟机
factory.setUsername("heima");//用户名
factory.setPassword("heima");//密码
//3.创建连接
Connection connection = factory.newConnection();
//4.创建channel
Channel channel = connection.createChannel();
//5.创建队列queue
/**
* queueDeclare(String queue, boolean durable, boolean exclusive,
* boolean autoDelete, Map<String, Object> arguments)
* 参数:
* 1.queue:队列名称
* 2.durable:是否持久化
* 3.exclusive
* *是否独占。只能有一个消费者监听这个队列
* *当connection关闭时,是否删除队列
* 4.autoDelete:是否自动删除,当没有consumer时,自动删除掉
* 5.arguments:参数信息
*/
//如果没有一个叫hello_world的队列,则会创建改队列,如果有则不会创建
channel.queueDeclare("work_queues",true,false,false,null);
/**
*
* public String basicConsume(String queue, boolean autoAck, Consumer callback)
* 1.queue:队列名称
* 2.autoAck:是否自动确认
* 3.callback:回调对象
*/
//6.发送消息
Consumer consumer = new DefaultConsumer(channel){
/**
*这是一个回调方法,收到消息后,自动执行该方法
*1.consumerTag:标记
* 2.envelope:获取一些信息,交换机,路由
* 3.properties:配置信息
* 4.body:数据
*/
@Override
public void handleDelivery(String consumerTag, Envelope envelope, AMQP.BasicProperties properties, byte[] body) throws IOException {
System.out.println("consumerTag:"+consumerTag);
System.out.println("Envelope:"+envelope.getExchange());
System.out.println("RoutingKey:"+envelope.getRoutingKey());
System.out.println("properties:"+properties);
System.out.println("body:"+new String(body));
}
};
channel.basicConsume("work_queues",true,consumer);
//7.消费者不用关闭资源
}
Consumer_WorkQueues2
public class Consumer_WorkQueues1 {
public static void main(String[] args) throws IOException, TimeoutException {
//1.创建连接工厂
ConnectionFactory factory = new ConnectionFactory();
//2.设置参数
factory.setHost("8.130.108.134");//ip
factory.setPort(5672);//端口
factory.setVirtualHost("/itcast");//虚拟机
factory.setUsername("heima");//用户名
factory.setPassword("heima");//密码
//3.创建连接
Connection connection = factory.newConnection();
//4.创建channel
Channel channel = connection.createChannel();
//5.创建队列queue
/**
* queueDeclare(String queue, boolean durable, boolean exclusive,
* boolean autoDelete, Map<String, Object> arguments)
* 参数:
* 1.queue:队列名称
* 2.durable:是否持久化
* 3.exclusive
* *是否独占。只能有一个消费者监听这个队列
* *当connection关闭时,是否删除队列
* 4.autoDelete:是否自动删除,当没有consumer时,自动删除掉
* 5.arguments:参数信息
*/
//如果没有一个叫hello_world的队列,则会创建改队列,如果有则不会创建
channel.queueDeclare("work_queues",true,false,false,null);
/**
*
* public String basicConsume(String queue, boolean autoAck, Consumer callback)
* 1.queue:队列名称
* 2.autoAck:是否自动确认
* 3.callback:回调对象
*/
//6.发送消息
Consumer consumer = new DefaultConsumer(channel){
/**
*这是一个回调方法,收到消息后,自动执行该方法
*1.consumerTag:标记
* 2.envelope:获取一些信息,交换机,路由
* 3.properties:配置信息
* 4.body:数据
*/
@Override
public void handleDelivery(String consumerTag, Envelope envelope, AMQP.BasicProperties properties, byte[] body) throws IOException {
System.out.println("consumerTag:"+consumerTag);
System.out.println("Envelope:"+envelope.getExchange());
System.out.println("RoutingKey:"+envelope.getRoutingKey());
System.out.println("properties:"+properties);
System.out.println("body:"+new String(body));
}
};
channel.basicConsume("work_queues",true,consumer);
//7.消费者不用关闭资源
}
小结
4.2 Pub/Sub 订阅模式
- P:生产者,也就是要发送消息的程序,但是不再发送到队列中,而是发给X(交换机)
- C:消费者,消息的接收者,会一直等待消息到来
- Queue:消息队列,接收消息、缓存消息
- Exchange:交换机(X)。一方面,接收生产者发送的消息。另一方面,知道如何处理消息,例如递交给某个特别队列、 递交给所有队列、或是将消息丢弃。到底如何操作,取决于Exchange的类型。Exchange有常见以下3种类型:
- Fanout:广播,将消息交给所有绑定到交换机的队列
- Direct:定向,把消息交给符合指定routing key 的队列
- Topic:通配符,把消息交给符合routing pattern(路由模式) 的队列
public class Consumer_PubSub1 {
public static void main(String[] args) throws IOException, TimeoutException {
//1.创建连接工厂
ConnectionFactory factory = new ConnectionFactory();
//2.设置参数
factory.setHost("8.130.108.134");//ip
factory.setPort(5672);//端口
factory.setVirtualHost("/itcast");//虚拟机
factory.setUsername("heima");//用户名
factory.setPassword("heima");//密码
//3.创建连接
Connection connection = factory.newConnection();
//4.创建channel
Channel channel = connection.createChannel();
/**
*
* public String basicConsume(String queue, boolean autoAck, Consumer callback)
* 1.queue:队列名称
* 2.autoAck:是否自动确认
* 3.callback:回调对象
*/
//6.发送消息
Consumer consumer = new DefaultConsumer(channel){
/**
*这是一个回调方法,收到消息后,自动执行该方法
*1.consumerTag:标记
* 2.envelope:获取一些信息,交换机,路由
* 3.properties:配置信息
* 4.body:数据
*/
@Override
public void handleDelivery(String consumerTag, Envelope envelope, AMQP.BasicProperties properties, byte[] body) throws IOException {
System.out.println("body:"+new String(body));
System.out.println("将日志信息打印到控制台");
}
};
channel.basicConsume("test_fanout_queue1",true,consumer);
//7.消费者不用关闭资源
}
2.消费者
Consumer_PubSub1
public class Consumer_PubSub1 {
public static void main(String[] args) throws IOException, TimeoutException {
//1.创建连接工厂
ConnectionFactory factory = new ConnectionFactory();
//2.设置参数
factory.setHost("8.130.108.134");//ip
factory.setPort(5672);//端口
factory.setVirtualHost("/itcast");//虚拟机
factory.setUsername("heima");//用户名
factory.setPassword("heima");//密码
//3.创建连接
Connection connection = factory.newConnection();
//4.创建channel
Channel channel = connection.createChannel();
/**
*
* public String basicConsume(String queue, boolean autoAck, Consumer callback)
* 1.queue:队列名称
* 2.autoAck:是否自动确认
* 3.callback:回调对象
*/
//6.发送消息
Consumer consumer = new DefaultConsumer(channel){
/**
*这是一个回调方法,收到消息后,自动执行该方法
*1.consumerTag:标记
* 2.envelope:获取一些信息,交换机,路由
* 3.properties:配置信息
* 4.body:数据
*/
@Override
public void handleDelivery(String consumerTag, Envelope envelope, AMQP.BasicProperties properties, byte[] body) throws IOException {
System.out.println("body:"+new String(body));
System.out.println("将日志信息打印到控制台");
}
};
channel.basicConsume("test_fanout_queue1",true,consumer);
//7.消费者不用关闭资源
}
}
Consumer_PubSub2
public class Consumer_PubSub1 {
public static void main(String[] args) throws IOException, TimeoutException {
//1.创建连接工厂
ConnectionFactory factory = new ConnectionFactory();
//2.设置参数
factory.setHost("8.130.108.134");//ip
factory.setPort(5672);//端口
factory.setVirtualHost("/itcast");//虚拟机
factory.setUsername("heima");//用户名
factory.setPassword("heima");//密码
//3.创建连接
Connection connection = factory.newConnection();
//4.创建channel
Channel channel = connection.createChannel();
/**
*
* public String basicConsume(String queue, boolean autoAck, Consumer callback)
* 1.queue:队列名称
* 2.autoAck:是否自动确认
* 3.callback:回调对象
*/
//6.发送消息
Consumer consumer = new DefaultConsumer(channel){
/**
*这是一个回调方法,收到消息后,自动执行该方法
*1.consumerTag:标记
* 2.envelope:获取一些信息,交换机,路由
* 3.properties:配置信息
* 4.body:数据
*/
@Override
public void handleDelivery(String consumerTag, Envelope envelope, AMQP.BasicProperties properties, byte[] body) throws IOException {
System.out.println("body:"+new String(body));
System.out.println("将日志信息打印到控制台");
}
};
channel.basicConsume("test_fanout_queue1",true,consumer);
//7.消费者不用关闭资源
}
4.3 Routing 路由模式
模式说明
- 队列与交换机的绑定,不能是任意绑定了,而是要指定一个 RoutingKey(路由key)
- 消息的发送方在向 Exchange 发送消息时,也必须指定消息的 RoutingKey
- Exchange 不再把消息交给每一个绑定的队列,而是根据消息的 Routing Key 进行判断,只有队列的 Routingkey 与消息的 Routing key 完全一致,才会接收到消息
- P:生产者,向 Exchange 发送消息,发送消息时,会指定一个routing key
- X:Exchange(交换机),接收生产者的消息,然后把消息递交给与 routing key 完全匹配的队列
- C1:消费者,其所在队列指定了需要 routing key 为 error 的消息
- C2:消费者,其所在队列指定了需要 routing key 为 info、error、warning 的消息
1.发布者
Producer_Routing1
public class Producer_Routing {
public static void main(String[] args) throws IOException, TimeoutException {
//1. 创建连接工厂
ConnectionFactory factory = new ConnectionFactory();
//2.设置参数
factory.setHost("8.130.108.134");//ip
factory.setPort(5672);//端口
factory.setVirtualHost("/itcast");//虚拟机
factory.setUsername("heima");//用户名
factory.setPassword("heima");//密码
//3.创建连接connection
Connection connection = factory.newConnection();
//4.创建channel
Channel channel = connection.createChannel();
//5.创建交换机
/**
* exchangeDeclare(String exchange, BuiltinExchangeType type, boolean durable,
* boolean autoDelete,boolean Map<String, Object> arguments)
* 1.exchange:交换机名称
* 2.type:交换机类型
* DIRECT("direct"),:定向
* FANOUT("fanout"),:广播(扇形)
* TOPIC("topic"),:通配符
* HEADERS("headers");:参数匹配
* 3.durable:是否持久化
* 4.autoDelete:是否自动删除
* 5.internal:内部使用。一般false
* 6.arguments:参数
*/
channel.exchangeDeclare("test_direct", BuiltinExchangeType.DIRECT,true,false,false,null);
//6.创建队列
channel.queueDeclare("test_direct_queue1",true,false,false,null);
channel.queueDeclare("test_direct_queue2",true,false,false,null);
//7.绑定队列和交换机
/**
* queueBind(String queue, String exchange, String routingKey)
* 1.queue:队列名称
* 2.exchange:交换机名称
* 3.routingKey:路由器
* 如果交换机的类型为fanout,routingKey为null
*/
//队列1的绑定
channel.queueBind("test_direct_queue1","test_fanout","error");
//队列2的绑定
channel.queueBind("test_direct_queue2","test_fanout","info");
channel.queueBind("test_direct_queue2","test_fanout","error");
channel.queueBind("test_direct_queue2","test_fanout","warning");
/**
* basicPublish(String exchange, String routingKey, BasicProperties props, byte[] body)
* 1.exchange:交换机名称。简单模式下交换机会使用默认的“ ”
* 2.routingKey:路由名称
* 3.props:配置信息
* 4.body:发送的信息
*/
String body = "日志信息:张三调用了findAll方法...日志级别:info...";
//8.发送消息
channel.basicPublish("test_direct","info",null,body.getBytes());
//9.释放资源
channel.close();
connection.close();
}
}
2.消费者
Consumer_Routing2
public class Consumer_Routing1 {
public static void main(String[] args) throws IOException, TimeoutException {
//1.创建连接工厂
ConnectionFactory factory = new ConnectionFactory();
//2.设置参数
factory.setHost("8.130.108.134");//ip
factory.setPort(5672);//端口
factory.setVirtualHost("/itcast");//虚拟机
factory.setUsername("heima");//用户名
factory.setPassword("heima");//密码
//3.创建连接
Connection connection = factory.newConnection();
//4.创建channel
Channel channel = connection.createChannel();
/**
*
* public String basicConsume(String queue, boolean autoAck, Consumer callback)
* 1.queue:队列名称
* 2.autoAck:是否自动确认
* 3.callback:回调对象
*/
//6.发送消息
Consumer consumer = new DefaultConsumer(channel){
/**
*这是一个回调方法,收到消息后,自动执行该方法
*1.consumerTag:标记
* 2.envelope:获取一些信息,交换机,路由
* 3.properties:配置信息
* 4.body:数据
*/
@Override
public void handleDelivery(String consumerTag, Envelope envelope, AMQP.BasicProperties properties, byte[] body) throws IOException {
System.out.println("body:"+new String(body));
System.out.println("将日志信息打印到控制台");
}
};
channel.basicConsume("test_direct_queue1",true,consumer);
//7.消费者不用关闭资源
}
}
Consumer_Routing2
public class Consumer_Routing2 {
public static void main(String[] args) throws IOException, TimeoutException {
//1.创建连接工厂
ConnectionFactory factory = new ConnectionFactory();
//2.设置参数
factory.setHost("8.130.108.134");//ip
factory.setPort(5672);//端口
factory.setVirtualHost("/itcast");//虚拟机
factory.setUsername("heima");//用户名
factory.setPassword("heima");//密码
//3.创建连接
Connection connection = factory.newConnection();
//4.创建channel
Channel channel = connection.createChannel();
/**
*
* public String basicConsume(String queue, boolean autoAck, Consumer callback)
* 1.queue:队列名称
* 2.autoAck:是否自动确认
* 3.callback:回调对象
*/
//6.发送消息
Consumer consumer = new DefaultConsumer(channel){
/**
*这是一个回调方法,收到消息后,自动执行该方法
*1.consumerTag:标记
* 2.envelope:获取一些信息,交换机,路由
* 3.properties:配置信息
* 4.body:数据
*/
@Override
public void handleDelivery(String consumerTag, Envelope envelope, AMQP.BasicProperties properties, byte[] body) throws IOException {
System.out.println("body:"+new String(body));
System.out.println("将日志信息打印到控制台");
}
};
channel.basicConsume("test_direct_queue2",true,consumer);
//7.消费者不用关闭资源
}
}
3. 小结
4.4 Topics 通配符模式
- Topic 类型与 Direct 相比,都是可以根据 RoutingKey 把消息路由到不同的队列。只不过 Topic 类型Exchange 可以让队列在绑定 Routing key 的时候使用通配符!
- Routingkey 一般都是有一个或多个单词组成,多个单词之间以”.”分割,例如: item.insert
- 通配符规则:# 匹配一个或多个词,* 匹配不多不少恰好1个词,例如:item.# 能够匹配 item.insert.abc 或者 item.insert,item.* 只能匹配 item.insert
//需求:queue1Name所有error级别的日志存入数据库,所有order系统日志存入数据库 //需求:queue2Name所有*.*的数据都打印到控制台
1.发布者
Producer_Topics
public class Producer_Topics {
public static void main(String[] args) throws IOException, TimeoutException {
//1. 创建连接工厂
ConnectionFactory factory = new ConnectionFactory();
//2.设置参数
factory.setHost("8.130.108.134");//ip
factory.setPort(5672);//端口
factory.setVirtualHost("/itcast");//虚拟机
factory.setUsername("heima");//用户名
factory.setPassword("heima");//密码
//3.创建连接connection
Connection connection = factory.newConnection();
//4.创建channel
Channel channel = connection.createChannel();
//5.创建交换机
/**
* exchangeDeclare(String exchange, BuiltinExchangeType type, boolean durable,
* boolean autoDelete,boolean Map<String, Object> arguments)
* 1.exchange:交换机名称
* 2.type:交换机类型
* DIRECT("direct"),:定向
* FANOUT("fanout"),:广播(扇形)
* TOPIC("topic"),:通配符
* HEADERS("headers");:参数匹配
* 3.durable:是否持久化
* 4.autoDelete:是否自动删除
* 5.internal:内部使用。一般false
* 6.arguments:参数
*/
String exchangeName="test_topic";
channel.exchangeDeclare(exchangeName, BuiltinExchangeType.TOPIC,true,false,false,null);
//6.创建队列
String queue1Name="test_topic_queue1";
String queue2Name="test_topic_queue2";
channel.queueDeclare(queue1Name,true,false,false,null);
channel.queueDeclare(queue2Name,true,false,false,null);
//7.绑定队列和交换机
/**
* queueBind(String queue, String exchange, String routingKey)
* 1.queue:队列名称
* 2.exchange:交换机名称
* 3.routingKey:路由器
* 如果交换机的类型为fanout,routingKey为null
*/
//需求:queue1Name所有error级别的日志存入数据库,所有order系统日志存入数据库
//需求:queue2Name所有*.*的数据都打印到控制台
channel.queueBind(queue1Name,exchangeName,"#.error");
channel.queueBind(queue1Name,exchangeName,"order.*");
channel.queueBind(queue2Name,exchangeName,"*.*");
/**
* basicPublish(String exchange, String routingKey, BasicProperties props, byte[] body)
* 1.exchange:交换机名称。简单模式下交换机会使用默认的“ ”
* 2.routingKey:路由名称
* 3.props:配置信息
* 4.body:发送的信息
*/
String body = "日志信息:张三调用了findAll方法...日志级别:info...";
//8.发送消息
channel.basicPublish(exchangeName,"good.info",null,body.getBytes());
//9.释放资源
channel.close();
connection.close();
}
}
Consumer_Topic1
public class Consumer_Topic1 {
public static void main(String[] args) throws IOException, TimeoutException {
//1.创建连接工厂
ConnectionFactory factory = new ConnectionFactory();
//2.设置参数
factory.setHost("8.130.108.134");//ip
factory.setPort(5672);//端口
factory.setVirtualHost("/itcast");//虚拟机
factory.setUsername("heima");//用户名
factory.setPassword("heima");//密码
//3.创建连接
Connection connection = factory.newConnection();
//4.创建channel
Channel channel = connection.createChannel();
String queue1Name = "test_topic_queue1";
String queue2Name = "test_topic_queue2";
/**
*
* public String basicConsume(String queue, boolean autoAck, Consumer callback)
* 1.queue:队列名称
* 2.autoAck:是否自动确认
* 3.callback:回调对象
*/
//6.发送消息
Consumer consumer = new DefaultConsumer(channel){
/**
*这是一个回调方法,收到消息后,自动执行该方法
*1.consumerTag:标记
* 2.envelope:获取一些信息,交换机,路由
* 3.properties:配置信息
* 4.body:数据
*/
@Override
public void handleDelivery(String consumerTag, Envelope envelope, AMQP.BasicProperties properties, byte[] body) throws IOException {
System.out.println("body:"+new String(body));
System.out.println("将日志存到数据库");
}
};
channel.basicConsume(queue1Name,true,consumer);
//7.消费者不用关闭资源
}
Consumer_Topic2
public class Consumer_Topic2 {
public static void main(String[] args) throws IOException, TimeoutException {
//1.创建连接工厂
ConnectionFactory factory = new ConnectionFactory();
//2.设置参数
factory.setHost("8.130.108.134");//ip
factory.setPort(5672);//端口
factory.setVirtualHost("/itcast");//虚拟机
factory.setUsername("heima");//用户名
factory.setPassword("heima");//密码
//3.创建连接
Connection connection = factory.newConnection();
//4.创建channel
Channel channel = connection.createChannel();
String queue1Name = "test_topic_queue1";
String queue2Name = "test_topic_queue2";
/**
*
* public String basicConsume(String queue, boolean autoAck, Consumer callback)
* 1.queue:队列名称
* 2.autoAck:是否自动确认
* 3.callback:回调对象
*/
//6.发送消息
Consumer consumer = new DefaultConsumer(channel){
/**
*这是一个回调方法,收到消息后,自动执行该方法
*1.consumerTag:标记
* 2.envelope:获取一些信息,交换机,路由
* 3.properties:配置信息
* 4.body:数据
*/
@Override
public void handleDelivery(String consumerTag, Envelope envelope, AMQP.BasicProperties properties, byte[] body) throws IOException {
System.out.println("body:"+new String(body));
System.out.println("将日志存到数据库");
}
};
channel.basicConsume(queue2Name,true,consumer);
//7.消费者不用关闭资源
}
总结
4. SpringBoot 整合RabbitMQ
<dependencies>
<!-- rabbitmq的依赖-->
<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>
</dependencies>
2.application.yml
spring:
rabbitmq:
host: 8.130.108.134
username: itcast
password: 123321
virtual-host: /
port: 5672
3.RabbitMQConfig
@Configuration
public class RabbitMQConfig {
public static final String EXCHANGE_NAME = "boot_topic_exchange";
public static final String QUEUE_NAME = "boot_queue";
//1.交换机
@Bean("bootExchange")
public Exchange bootExchange(){
//名字和持久化
return ExchangeBuilder.topicExchange(EXCHANGE_NAME).durable(true).build();
}
//2.Queue 队列
@Bean("bootQueue")
public Queue bootQueue(){
return QueueBuilder.durable(QUEUE_NAME).build();
}
//3. 队列和交互机绑定关系 Binding
/*
1. 知道哪个队列
2. 知道哪个交换机
3. routing key
*/
@Bean
public Binding bindQueueExchange(@Qualifier("bootQueue") Queue queue, @Qualifier("bootExchange") Exchange exchange){
return BindingBuilder.bind(queue).to(exchange).with("boot.#").noargs();
}
}
4.ProducerTest
@SpringBootTest
@RunWith(SpringRunner.class)
public class ProducerTest {
//1.注入RabbitTemplate
@Autowired
private RabbitTemplate rabbitTemplate;
@Test
public void testSend(){
rabbitTemplate.convertAndSend(RabbitMQConfig.EXCHANGE_NAME,"boot.haha","boot mq hello~~~");
}
<dependencies>
<!-- rabbitmq的依赖-->
<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>
</dependencies>
2.application.yml
spring:
rabbitmq:
host: 8.130.108.134
username: itcast
password: 123321
virtual-host: /
port: 5672
3.RabbitMQListener
@Component
public class RabbitMQListener {
@RabbitListener(queues ="boot_queue")
// 自动监听队列的信息到方法参数
public void ListenerQueue(Message message){
System.out.println(new String(message.getBody()));
}
}
小结
- SpringBoot提供了快速整合RabbitMQ的方式
- 基本信息再yml中配置,队列交互机以及绑定关系在配置类中使用Bean的方式配置
- 生产端直接注入RabbitTemplate完成消息发送
- 消费端直接使用@RabbitListener完成消息接收
1. RabbitMQ 高级特性
1.1 消息的可靠投递
- ⚫ confirm 确认模式
- ⚫ return 退回模式
- ⚫ 消息从 producer 到 exchange 则会返回一个 confirmCallback 。
- ⚫ 消息从 exchange-->queue 投递失败则会返回一个 returnCallback 。
步骤
1.2 Consumer Ack
- • 自动确认:acknowledge="none"
- • 手动确认:acknowledge="manual"
- • 根据异常情况确认:acknowledge="auto",(这种方式使用麻烦,不作讲解)
1.3 消费端限流
- 在<rabbit:listener-container> 中配置 prefetch属性设置消费端一次拉取多少消息
- 消费端的确认模式一定为手动确认。acknowledge="manual
1.4 TTL
- TTL 全称 Time To Live(存活时间/过期时间)。
- 当消息到达存活时间后,还没有被消费,会被自动清除。
- RabbitMQ可以对消息设置过期时间,也可以对整个队列(Queue)设置过期时间。
- 设置队列过期时间使用参数:x-message-ttl,单位:ms(毫秒),会对整个队列消息统一过期。
- 设置消息过期时间使用参数:expiration。单位:ms(毫秒),当该消息在队列头部时(消费时),会单独判断 这一消息是否过期。
- 如果两者都进行了设置,以时间短的为准。
1.5 死信队列