本文章根据b站黑马教育-rabbitMQ消息中间件视频总结所写,如有需求,请通过如下链接观看视频
2020黑马——消息中间件RabbitMQ
MQ 全称Message Queue(消息队列),是在消息的传输过程中保存消息的容器。多用于分布式系统之间进行通信。
分布式系统子系统间两种通信方式
小结:
1、MQ,消息队列,存储消息的中间件;
2、分布式系统通信两种方式:直接远程调用 和 借助第三方 完成间接通信
3、发送方成为生产者,接收方称为消费者
优势:
- 应用解耦
- 异步提速
- 削峰填谷
劣势:
- 系统可用性降低
- 系统复杂度提高
- 一致性问题
系统的耦合性越高,容错性就越低,可维护性就越低。
一个下单操作耗时:20+300+300+300 = 920ms
用户点击完下单按钮后,需要等待920ms才能得到下单响应,太慢!
用户点击完下单按钮后,只需等待25ms就能得到下单响应(20+5 = 25ms)
提升用户体验和系统吞吐量(单位时间内处理请求的数目)。
MQ传递
使用了MQ之后,限制消费小溪的速度为1000,这样一来,高峰期产生的数据势必会挤压在MQ中,高峰就被“削”掉了,但是因为消息积压,在高峰期过后的一段时间内,消费消息的速度还是会维持在1000,直到消费完积压的消息,这就叫做“填谷”。
使用MQ后,可以提高系统稳定性。
小结:
1、应用解耦:提高系统容错性和可维护性
2、异步提速:提升用户体验和系统吞吐量
3、削峰填谷:提高系统稳定性。
系统可用性降低
系统引入的外部依赖越多,系统稳定性越差。一旦MQ宕机,就会对业务造成影响。如何保证MQ的高可用?
系统复杂度提高
MQ的加入大大增加了系统的复杂度,以前系统间时同步的远程调用,现在是通过MQ异步调用。如何保证消息没有被重复消
费?怎样处理消息丢失情况?怎么保证消息传递的顺序性?
一致性问题
A系统处理完业务,通过MQ给B、C、D三个系统发消息数据,如果B系统、C系统处理成功,D系统处理失败。如何保证消息数据处理的一致性?
小结:
既然MQ有优势也有劣势,那么使用MQ需要满足什么条件呢?
① 生产者不需要从消费者出获得反馈。引入消息队列之前的直接调用,其接口的返回值应该为null,这才让明白下层的动作还没做,上层却当成动作做完了继续往后走,即所谓异步成为了可能。
②容许短暂的不一致性。
③确实用了有效果。即解耦、提速、削峰这些方面的收益,超过加入MQ,管理MQ这些成本。
目前业界有很多的MQ产品,例如RabbitMQ、RocketMQ、ActiveMQ、Kafka、ZeroMQ,MetaMq等,也有直接使用Redis充当消息队列的案例,而这些消息队列产品,各有侧重,在实际选型时,需要结合自身需求及MQ产品特征,综合考虑。
由于RabbitMQ综合能力强劲,所以接下来的课程中,我们将主要学习RabbitMQ。
AMQP,即 Advanced Message Queuing Protocol(高级消息队列协议),是一个网络协议,是应用层协议的一个开放标准,为面向消息的中间件设计。给予此协议的客户端与消息中间件可传递消息,并不受客户端/中间件不同产品,不同的开发语言等条件的限制。2006年,AMQP规范发布。类比http
2007年,Rabbit计数公司给予AMQP标准开发的RabbitMQ 1.0 发布。RabbitMQ采用Erlang语言开发。
Erlang语言由Ericson设计,专门为开发高并发和分布式系统的一种语言,在典型领域使用广泛。
RabbitMQ基础架构如下图:
RabbitMQ中的相关概念:
RabbitMQ提供了6中工作模式:简单模式、work queues、Publish/Subscribe发布与订阅模式、Routing路由模式、Topics主题模式、RPC远程调用模式(远程调用,不太算MQ;暂不做介绍)。
官网对应模式介绍:https://www.rabbitmq.com/getstarted.html
小结:
1、RabbitMQ是基于AMQP协议使用Erlang语言开发的一款消息队列产品。
2、RabbitMQ提供了6中工作模式,我们学习5中。这是今天的重点。
3、AMQP是协议,类比HTTP。
JMS是API规范接口,类比JDBC。
需求:使用简单模式完成消息传递
步骤:
① 创建工程(生产者、消费者)
② 分别添加依赖
③ 编写生产者发送消息
④ 编写消费者接收消息
Producer_HelloWorld.java
package com.ph.producer;
import com.rabbitmq.client.Channel;
import com.rabbitmq.client.Connection;
import com.rabbitmq.client.ConnectionFactory;
import java.io.IOException;
import java.nio.charset.StandardCharsets;
import java.util.concurrent.TimeoutException;
public class Producer_HelloWorld {
public static void main(String[] args) throws IOException, TimeoutException {
//1、创建连接工厂
ConnectionFactory factory = new ConnectionFactory();
//2、设置参数
factory.setHost("172.16.98.133");//ip 默认值 localhost
factory.setPort(5672);//端口 默认值5672
factory.setVirtualHost("/itcast");//虚拟机 ,默认值/
factory.setUsername("heima");//用户名 默认 guest
factory.setPassword("heima");//密码 默认 guest
//3、创建连接 Connection
Connection connection = factory.newConnection();
//4、常见Channel
Channel channel = connection.createChannel();
//5、创建队列Queue
/*
* queueDeclare(String queue, boolean durable, boolean exclusive, boolean autoDelete, Map arguments)
* 参数:
* 1.queue:队列名称
* 2.durable:是否持久化,当mq重启之后,还在
* 3。exclusive:
* *是否独占。只能有一个消费者监听这队列
* *当Connection关闭时,是否删除队列
* 4、autoDelete:是否自动删除。当没有Consumer时,自动删除掉
* 5、arguments:参数
* */
//入门没有一个名字叫hello_world的队列,则会创建该队列,如果有则不会创建
channel.queueDeclare("hello_world",true,false,false,null);
//6、发送消息
/*
* basicPublish(String exchange, String routingKey, BasicProperties props, byte[] body) throws IOException {
* 参数:
* 1、exchange:交换机名称。简单模式下交换机会使用默认的""
* 2、routingKey:路由名称
* 3、props:配置信息
* 4、body:发送消息数据
* */
String body = "hello rabbitMQ!";
channel.basicPublish("","hello_world",null,body.getBytes());
//7、释放资源
channel.close();
connection.close();
}
}
Consumer_HelloWorld.java
package org.example;
import com.rabbitmq.client.*;
import java.io.IOException;
import java.util.concurrent.TimeoutException;
public class Consumer_HelloWorld {
public static void main(String[] args) throws IOException, TimeoutException {
//1、创建连接工厂
ConnectionFactory factory = new ConnectionFactory();
//2、设置参数
factory.setHost("172.16.98.133");//ip 默认值 localhost
factory.setPort(5672);//端口 默认值5672
factory.setVirtualHost("/itcast");//虚拟机 ,默认值/
factory.setUsername("heima");//用户名 默认 guest
factory.setPassword("heima");//密码 默认 guest
//3、创建连接 Connection
Connection connection = factory.newConnection();
//4、常见Channel
Channel channel = connection.createChannel();
//5、创建队列Queue
/*
* queueDeclare(String queue, boolean durable, boolean exclusive, boolean autoDelete, Map arguments)
* 参数:
* 1.queue:队列名称
* 2.durable:是否持久化,当mq重启之后,还在
* 3。exclusive:
* *是否独占。只能有一个消费者监听这队列
* *当Connection关闭时,是否删除队列
* 4、autoDelete:是否自动删除。当没有Consumer时,自动删除掉
* 5、arguments:参数
* */
//入门没有一个名字叫hello_world的队列,则会创建该队列,如果有则不会创建
channel.queueDeclare("hello_world",true,false,false,null);
//接收消息
/*
* basicConsume(String queue, boolean autoAck, Consumer callback)
* 参数:
* 1、queue:队列名称
* 2、autoAck:是否自动确认
* 3、callback:回调对象
* */
Consumer consumer = new DefaultConsumer(channel){
/*
* 回调方法,当收到消息后,会自动执行该方法
* 1、consumerTag:标识
* 2、envelope:获取一些信息,交换机、路由key...
* 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("Exchange"+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);
//消费者不用关闭资源链接,消费者相当于监听程序
}
}
上述的入门案例中其实使用的是如下的简单模式:
在上图的模型中,有以下概念:
① P:生产者,也就是要发送消息的程序
② C:消费者:消息的接收者,会一直等待消息到来
③ queue:消息队列,图中红色部分。类似一个邮箱,可以缓存消息;生产者向其中投递消息,消费者从其中取出消息
代码:
Producer_WorkQueues.java
package com.ph.producer;
import com.rabbitmq.client.Channel;
import com.rabbitmq.client.Connection;
import com.rabbitmq.client.ConnectionFactory;
import java.io.IOException;
import java.util.concurrent.TimeoutException;
public class Producer_WorkQueues {
public static void main(String[] args) throws IOException, TimeoutException {
//1、创建连接工厂
ConnectionFactory factory = new ConnectionFactory();
//2、设置参数
factory.setHost("172.16.98.133");//ip 默认值 localhost
factory.setPort(5672);//端口 默认值5672
factory.setVirtualHost("/itcast");//虚拟机 ,默认值/
factory.setUsername("heima");//用户名 默认 guest
factory.setPassword("heima");//密码 默认 guest
//3、创建连接 Connection
Connection connection = factory.newConnection();
//4、常见Channel
Channel channel = connection.createChannel();
//5、创建队列Queue
/*
* queueDeclare(String queue, boolean durable, boolean exclusive, boolean autoDelete, Map arguments)
* 参数:
* 1.queue:队列名称
* 2.durable:是否持久化,当mq重启之后,还在
* 3。exclusive:
* *是否独占。只能有一个消费者监听这队列
* *当Connection关闭时,是否删除队列
* 4、autoDelete:是否自动删除。当没有Consumer时,自动删除掉
* 5、arguments:参数
* */
//如果没有一个名字叫hello_world的队列,则会创建该队列,如果有则不会创建
channel.queueDeclare("work_queues",true,false,false,null);
//6、发送消息
/*
* basicPublish(String exchange, String routingKey, BasicProperties props, byte[] body) throws IOException {
* 参数:
* 1、exchange:交换机名称。简单模式下交换机会使用默认的""
* 2、routingKey:路由名称
* 3、props:配置信息
* 4、body:发送消息数据
* */
for (int i = 0; i <= 10; i++) {
String body = i+"=>hello rabbitMQ work_queues!";
channel.basicPublish("","work_queues",null,body.getBytes());
}
//7、释放资源
channel.close();
connection.close();
}
}
Consumer_WorkQueues1.java
package org.example;
import com.rabbitmq.client.*;
import java.io.IOException;
import java.util.concurrent.TimeoutException;
public class Consumer_WorkQueues1 {
public static void main(String[] args) throws IOException, TimeoutException {
//1、创建连接工厂
ConnectionFactory factory = new ConnectionFactory();
//2、设置参数
factory.setHost("172.16.98.133");//ip 默认值 localhost
factory.setPort(5672);//端口 默认值5672
factory.setVirtualHost("/itcast");//虚拟机 ,默认值/
factory.setUsername("heima");//用户名 默认 guest
factory.setPassword("heima");//密码 默认 guest
//3、创建连接 Connection
Connection connection = factory.newConnection();
//4、常见Channel
Channel channel = connection.createChannel();
//接收消息
/*
* basicConsume(String queue, boolean autoAck, Consumer callback)
* 参数:
* 1、queue:队列名称
* 2、autoAck:是否自动确认
* 3、callback:回调对象
* */
Consumer consumer = new DefaultConsumer(channel){
/*
* 回调方法,当收到消息后,会自动执行该方法
* 1、consumerTag:标识
* 2、envelope:获取一些信息,交换机、路由key...
* 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("Exchange"+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);
//消费者不用关闭资源链接,消费者相当于监听程序
}
}
Consumer_WorkQueues2.java
package org.example;
import com.rabbitmq.client.*;
import java.io.IOException;
import java.util.concurrent.TimeoutException;
public class Consumer_WorkQueues2 {
public static void main(String[] args) throws IOException, TimeoutException {
//1、创建连接工厂
ConnectionFactory factory = new ConnectionFactory();
//2、设置参数
factory.setHost("172.16.98.133");//ip 默认值 localhost
factory.setPort(5672);//端口 默认值5672
factory.setVirtualHost("/itcast");//虚拟机 ,默认值/
factory.setUsername("heima");//用户名 默认 guest
factory.setPassword("heima");//密码 默认 guest
//3、创建连接 Connection
Connection connection = factory.newConnection();
//4、常见Channel
Channel channel = connection.createChannel();
//接收消息
/*
* basicConsume(String queue, boolean autoAck, Consumer callback)
* 参数:
* 1、queue:队列名称
* 2、autoAck:是否自动确认
* 3、callback:回调对象
* */
Consumer consumer = new DefaultConsumer(channel){
/*
* 回调方法,当收到消息后,会自动执行该方法
* 1、consumerTag:标识
* 2、envelope:获取一些信息,交换机、路由key...
* 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("Exchange"+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);
//消费者不用关闭资源链接,消费者相当于监听程序
}
}
小结:
1、在一个队列中如果有多个消费者,那么消费者之间对于同一个消息的关系是竞争关系。
2、Work Queues对于任务过重或任务较多情况使用工作队列模式可以提高任务处理的速度。例如:短信服务部署多个,需要有一个节点发送即可。
在订阅模型中,多了一个Exchange角色,而且过程略有变化:
Producer_SubPub .java
package com.ph.producer;
import com.rabbitmq.client.BuiltinExchangeType;
import com.rabbitmq.client.Channel;
import com.rabbitmq.client.Connection;
import com.rabbitmq.client.ConnectionFactory;
import java.io.IOException;
import java.util.concurrent.TimeoutException;
public class Producer_SubPub {
public static void main(String[] args) throws IOException, TimeoutException {
//1、创建连接工厂
ConnectionFactory factory = new ConnectionFactory();
//2、设置参数
factory.setHost("172.16.98.133");//ip 默认值 localhost
factory.setPort(5672);//端口 默认值5672
factory.setVirtualHost("/itcast");//虚拟机 ,默认值/
factory.setUsername("heima");//用户名 默认 guest
factory.setPassword("heima");//密码 默认 guest
//3、创建连接 Connection
Connection connection = factory.newConnection();
//4、创建Channel
Channel channel = connection.createChannel();
//5、创建交换机
/*exchangeDeclare(String exchange,
BuiltinExchangeType type,
boolean durable,
boolean autoDelete,
boolean internal,
Map arguments)
参数:
1、exchange:交换机名称
2、type:交换机类型
(1) DIRECT("direct"):定向
(2)FANOUT("fanout"):扇形(广播),发送消息到每一个与之绑定的队列。
(3)TOPIC("topic"):通配符的形式
(4)HEADERS("headers"):参数匹配
3、durable:是否持久化
4、autoDelete:自动删除
5、internal:内部使用。一般为false
6、arguments:参数
*/
String exchangeName = "test_fanout";
channel.exchangeDeclare(exchangeName,BuiltinExchangeType.FANOUT,true,false,false,null);
//6、创建队列
String queue1Name = "test_fanout_queue1";
String queue2Name = "test_fanout_queue2";
channel.queueDeclare(queue1Name,true,false,false,null);
channel.queueDeclare(queue2Name,true,false,false,null);
//7、绑定队列和交换机
/*
queueBind(String queue, String exchange, String routingKey, Map arguments)
参数:
1、queue:队列名称;
2、exchange:交换机名称;
3、routingKey:路由键,绑定规则:
如果交换机的类型为fanout,routingKey设置为“”字符串,表示消息会发给每一个与之绑定的queue
4、arguments:参数
*/
channel.queueBind(queue1Name,exchangeName,"",null);
channel.queueBind(queue2Name,exchangeName,"",null);
//8、发送消息
String body = "日志信息:张三调用了findAll方法,日志级别:info";
channel.basicPublish(exchangeName,"",null,body.getBytes());
//9、释放资源
channel.close();
connection.close();
}
}
Consumer_SubPub1 .java
package org.example;
import com.rabbitmq.client.*;
import java.io.IOException;
import java.util.concurrent.TimeoutException;
public class Consumer_SubPub1 {
public static void main(String[] args) throws IOException, TimeoutException {
//1、创建连接工厂
ConnectionFactory factory = new ConnectionFactory();
//2、设置参数
factory.setHost("172.16.98.133");//ip 默认值 localhost
factory.setPort(5672);//端口 默认值5672
factory.setVirtualHost("/itcast");//虚拟机 ,默认值/
factory.setUsername("heima");//用户名 默认 guest
factory.setPassword("heima");//密码 默认 guest
//3、创建连接 Connection
Connection connection = factory.newConnection();
//4、常见Channel
Channel channel = connection.createChannel();
String queue1Name = "test_fanout_queue1";
String queue2Name = "test_fanout_queue2";
//接收消息
/*
* basicConsume(String queue, boolean autoAck, Consumer callback)
* 参数:
* 1、queue:队列名称
* 2、autoAck:是否自动确认
* 3、callback:回调对象
* */
Consumer consumer = new DefaultConsumer(channel){
/*
* 回调方法,当收到消息后,会自动执行该方法
* 1、consumerTag:标识
* 2、envelope:获取一些信息,交换机、路由key...
* 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("Exchange"+envelope.getExchange());
System.out.println("RoutingKey"+envelope.getRoutingKey());
System.out.println("properties"+properties);*/
System.out.println("body"+new String(body));
System.out.println("将日志信息打印到控制台");
}
};
channel.basicConsume(queue1Name,true,consumer);
//消费者不用关闭资源链接,消费者相当于监听程序
}
}
Consumer_SubPub2 .java
package org.example;
import com.rabbitmq.client.*;
import java.io.IOException;
import java.util.concurrent.TimeoutException;
public class Consumer_SubPub2 {
public static void main(String[] args) throws IOException, TimeoutException {
//1、创建连接工厂
ConnectionFactory factory = new ConnectionFactory();
//2、设置参数
factory.setHost("172.16.98.133");//ip 默认值 localhost
factory.setPort(5672);//端口 默认值5672
factory.setVirtualHost("/itcast");//虚拟机 ,默认值/
factory.setUsername("heima");//用户名 默认 guest
factory.setPassword("heima");//密码 默认 guest
//3、创建连接 Connection
Connection connection = factory.newConnection();
//4、常见Channel
Channel channel = connection.createChannel();
String queue1Name = "test_fanout_queue1";
String queue2Name = "test_fanout_queue2";
//接收消息
/*
* basicConsume(String queue, boolean autoAck, Consumer callback)
* 参数:
* 1、queue:队列名称
* 2、autoAck:是否自动确认
* 3、callback:回调对象
* */
Consumer consumer = new DefaultConsumer(channel){
/*
* 回调方法,当收到消息后,会自动执行该方法
* 1、consumerTag:标识
* 2、envelope:获取一些信息,交换机、路由key...
* 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("Exchange"+envelope.getExchange());
System.out.println("RoutingKey"+envelope.getRoutingKey());
System.out.println("properties"+properties);*/
System.out.println("body"+new String(body));
System.out.println("将日志信息保存数据库");
}
};
channel.basicConsume(queue2Name,true,consumer);
//消费者不用关闭资源链接,消费者相当于监听程序
}
}
1、模式说明:
图解:
小结:
Routing模式要求队列在绑定交换机时要指定routing key,消息会转发到符合routing key的队列
Producer_Routing .java
package com.ph.producer;
import com.rabbitmq.client.BuiltinExchangeType;
import com.rabbitmq.client.Channel;
import com.rabbitmq.client.Connection;
import com.rabbitmq.client.ConnectionFactory;
import java.io.IOException;
import java.util.concurrent.TimeoutException;
public class Producer_Routing {
public static void main(String[] args) throws IOException, TimeoutException {
//1、创建连接工厂
ConnectionFactory factory = new ConnectionFactory();
//2、设置参数
factory.setHost("172.16.98.133");//ip 默认值 localhost
factory.setPort(5672);//端口 默认值5672
factory.setVirtualHost("/itcast");//虚拟机 ,默认值/
factory.setUsername("heima");//用户名 默认 guest
factory.setPassword("heima");//密码 默认 guest
//3、创建连接 Connection
Connection connection = factory.newConnection();
//4、创建Channel
Channel channel = connection.createChannel();
//5、创建交换机
/*exchangeDeclare(String exchange,
BuiltinExchangeType type,
boolean durable,
boolean autoDelete,
boolean internal,
Map arguments)
参数:
1、exchange:交换机名称
2、type:交换机类型
(1) DIRECT("direct"):定向
(2)FANOUT("fanout"):扇形(广播),发送消息到每一个与之绑定的队列。
(3)TOPIC("topic"):通配符的形式
(4)HEADERS("headers"):参数匹配
3、durable:是否持久化
4、autoDelete:自动删除
5、internal:内部使用。一般为false
6、arguments:参数
*/
String exchangeName = "test_direct";
channel.exchangeDeclare(exchangeName,BuiltinExchangeType.DIRECT,true,false,false,null);
//6、创建队列
String queue1Name = "test_direct_queue1";
String queue2Name = "test_direct_queue2";
channel.queueDeclare(queue1Name,true,false,false,null);
channel.queueDeclare(queue2Name,true,false,false,null);
//7、绑定队列和交换机
/*
queueBind(String queue, String exchange, String routingKey, Map arguments)
参数:
1、queue:队列名称;
2、exchange:交换机名称;
3、routingKey:路由键,绑定规则:
如果交换机的类型为fanout,routingKey设置为“”字符串,表示消息会发给每一个与之绑定的queue
4、arguments:参数
*/
//队列1的绑定 error
channel.queueBind(queue1Name,exchangeName,"error",null);
//队列2的绑定 info error warning
channel.queueBind(queue2Name,exchangeName,"info",null);
channel.queueBind(queue2Name,exchangeName,"error",null);
channel.queueBind(queue2Name,exchangeName,"warning",null);
//8、发送消息
String body = "日志信息:张三调用了findAll方法,日志级别:info";
channel.basicPublish(exchangeName,"info",null,body.getBytes());
//9、释放资源
channel.close();
connection.close();
}
}
Consumer_Routing1 .java
package org.example;
import com.rabbitmq.client.*;
import java.io.IOException;
import java.util.concurrent.TimeoutException;
public class Consumer_Routing1 {
public static void main(String[] args) throws IOException, TimeoutException {
//1、创建连接工厂
ConnectionFactory factory = new ConnectionFactory();
//2、设置参数
factory.setHost("172.16.98.133");//ip 默认值 localhost
factory.setPort(5672);//端口 默认值5672
factory.setVirtualHost("/itcast");//虚拟机 ,默认值/
factory.setUsername("heima");//用户名 默认 guest
factory.setPassword("heima");//密码 默认 guest
//3、创建连接 Connection
Connection connection = factory.newConnection();
//4、常见Channel
Channel channel = connection.createChannel();
String queue1Name = "test_direct_queue1";
String queue2Name = "test_direct_queue2";
//接收消息
/*
* basicConsume(String queue, boolean autoAck, Consumer callback)
* 参数:
* 1、queue:队列名称
* 2、autoAck:是否自动确认
* 3、callback:回调对象
* */
Consumer consumer = new DefaultConsumer(channel){
/*
* 回调方法,当收到消息后,会自动执行该方法
* 1、consumerTag:标识
* 2、envelope:获取一些信息,交换机、路由key...
* 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("Exchange"+envelope.getExchange());
System.out.println("RoutingKey"+envelope.getRoutingKey());
System.out.println("properties"+properties);*/
System.out.println("body"+new String(body));
System.out.println("将日志信息打印到控制台");
}
};
channel.basicConsume(queue1Name,true,consumer);
//消费者不用关闭资源链接,消费者相当于监听程序
}
}
Consumer_Routing2 .java
package org.example;
import com.rabbitmq.client.*;
import java.io.IOException;
import java.util.concurrent.TimeoutException;
public class Consumer_Routing2 {
public static void main(String[] args) throws IOException, TimeoutException {
//1、创建连接工厂
ConnectionFactory factory = new ConnectionFactory();
//2、设置参数
factory.setHost("172.16.98.133");//ip 默认值 localhost
factory.setPort(5672);//端口 默认值5672
factory.setVirtualHost("/itcast");//虚拟机 ,默认值/
factory.setUsername("heima");//用户名 默认 guest
factory.setPassword("heima");//密码 默认 guest
//3、创建连接 Connection
Connection connection = factory.newConnection();
//4、常见Channel
Channel channel = connection.createChannel();
String queue1Name = "test_direct_queue1";
String queue2Name = "test_direct_queue2";
//接收消息
/*
* basicConsume(String queue, boolean autoAck, Consumer callback)
* 参数:
* 1、queue:队列名称
* 2、autoAck:是否自动确认
* 3、callback:回调对象
* */
Consumer consumer = new DefaultConsumer(channel){
/*
* 回调方法,当收到消息后,会自动执行该方法
* 1、consumerTag:标识
* 2、envelope:获取一些信息,交换机、路由key...
* 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("Exchange"+envelope.getExchange());
System.out.println("RoutingKey"+envelope.getRoutingKey());
System.out.println("properties"+properties);*/
System.out.println("body"+new String(body));
System.out.println("将日志信息打印到控制台");
}
};
channel.basicConsume(queue2Name,true,consumer);
//消费者不用关闭资源链接,消费者相当于监听程序
}
}
1、模式说明
小结:
Topics模式可以实现Pub/Sub发布与订阅模式和Routing路由模式的功能,只是Topics在配置routing key的时候可以使用通配符,显得更加灵活
Producer_Topics .java
package com.ph.producer;
import com.rabbitmq.client.BuiltinExchangeType;
import com.rabbitmq.client.Channel;
import com.rabbitmq.client.Connection;
import com.rabbitmq.client.ConnectionFactory;
import java.io.IOException;
import java.util.concurrent.TimeoutException;
public class Producer_Topics {
public static void main(String[] args) throws IOException, TimeoutException {
//1、创建连接工厂
ConnectionFactory factory = new ConnectionFactory();
//2、设置参数
factory.setHost("172.16.98.133");//ip 默认值 localhost
factory.setPort(5672);//端口 默认值5672
factory.setVirtualHost("/itcast");//虚拟机 ,默认值/
factory.setUsername("heima");//用户名 默认 guest
factory.setPassword("heima");//密码 默认 guest
//3、创建连接 Connection
Connection connection = factory.newConnection();
//4、创建Channel
Channel channel = connection.createChannel();
//5、创建交换机
/*exchangeDeclare(String exchange,
BuiltinExchangeType type,
boolean durable,
boolean autoDelete,
boolean internal,
Map arguments)
参数:
1、exchange:交换机名称
2、type:交换机类型
(1) DIRECT("direct"):定向
(2)FANOUT("fanout"):扇形(广播),发送消息到每一个与之绑定的队列。
(3)TOPIC("topic"):通配符的形式
(4)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, Map arguments)
参数:
1、queue:队列名称;
2、exchange:交换机名称;
3、routingKey:路由键,绑定规则:
如果交换机的类型为fanout,routingKey设置为“”字符串,表示消息会发给每一个与之绑定的queue
4、arguments:参数
*/
//routing key 系统的名称.日志的级别
//需求:所有error级别的日志存入数据库,所有order系统的日志存入数据库
channel.queueBind(queue1Name,exchangeName,"#.error",null);
channel.queueBind(queue1Name,exchangeName,"order.*",null);
channel.queueBind(queue2Name,exchangeName,"*.*",null);
//8、发送消息
String body = "日志信息:张三调用了findAll方法,日志级别:info";
channel.basicPublish(exchangeName,"order.error",null,body.getBytes());
//9、释放资源
channel.close();
connection.close();
}
}
Consumer_Topics1 .java
package org.example;
import com.rabbitmq.client.*;
import java.io.IOException;
import java.util.concurrent.TimeoutException;
public class Consumer_Topics1 {
public static void main(String[] args) throws IOException, TimeoutException {
//1、创建连接工厂
ConnectionFactory factory = new ConnectionFactory();
//2、设置参数
factory.setHost("172.16.98.133");//ip 默认值 localhost
factory.setPort(5672);//端口 默认值5672
factory.setVirtualHost("/itcast");//虚拟机 ,默认值/
factory.setUsername("heima");//用户名 默认 guest
factory.setPassword("heima");//密码 默认 guest
//3、创建连接 Connection
Connection connection = factory.newConnection();
//4、常见Channel
Channel channel = connection.createChannel();
String queue1Name = "test_topic_queue1";
String queue2Name = "test_topic_queue2";
//接收消息
/*
* basicConsume(String queue, boolean autoAck, Consumer callback)
* 参数:
* 1、queue:队列名称
* 2、autoAck:是否自动确认
* 3、callback:回调对象
* */
Consumer consumer = new DefaultConsumer(channel){
/*
* 回调方法,当收到消息后,会自动执行该方法
* 1、consumerTag:标识
* 2、envelope:获取一些信息,交换机、路由key...
* 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("Exchange"+envelope.getExchange());
System.out.println("RoutingKey"+envelope.getRoutingKey());
System.out.println("properties"+properties);*/
System.out.println("body"+new String(body));
System.out.println("将日志信息打印到控制台");
}
};
channel.basicConsume(queue1Name,true,consumer);
//消费者不用关闭资源链接,消费者相当于监听程序
}
}
Consumer_Topics2 .java
package org.example;
import com.rabbitmq.client.*;
import java.io.IOException;
import java.util.concurrent.TimeoutException;
public class Consumer_Topics2 {
public static void main(String[] args) throws IOException, TimeoutException {
//1、创建连接工厂
ConnectionFactory factory = new ConnectionFactory();
//2、设置参数
factory.setHost("172.16.98.133");//ip 默认值 localhost
factory.setPort(5672);//端口 默认值5672
factory.setVirtualHost("/itcast");//虚拟机 ,默认值/
factory.setUsername("heima");//用户名 默认 guest
factory.setPassword("heima");//密码 默认 guest
//3、创建连接 Connection
Connection connection = factory.newConnection();
//4、常见Channel
Channel channel = connection.createChannel();
String queue1Name = "test_topic_queue1";
String queue2Name = "test_topic_queue2";
//接收消息
/*
* basicConsume(String queue, boolean autoAck, Consumer callback)
* 参数:
* 1、queue:队列名称
* 2、autoAck:是否自动确认
* 3、callback:回调对象
* */
Consumer consumer = new DefaultConsumer(channel){
/*
* 回调方法,当收到消息后,会自动执行该方法
* 1、consumerTag:标识
* 2、envelope:获取一些信息,交换机、路由key...
* 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("Exchange"+envelope.getExchange());
System.out.println("RoutingKey"+envelope.getRoutingKey());
System.out.println("properties"+properties);*/
System.out.println("body"+new String(body));
System.out.println("将日志信息打印到控制台");
}
};
channel.basicConsume(queue2Name,true,consumer);
//消费者不用关闭资源链接,消费者相当于监听程序
}
}
需求:使用spring整合RabbitMQ
步骤:
生产者:
①创建生产者工程
②添加依赖
③配置整合
④编写代码发送消息
消费者
①创建消费者工程
②添加依赖
③配置整合
④编写消息监听器
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0modelVersion>
<groupId>org.examplegroupId>
<artifactId>spring-rabbitmq-producersartifactId>
<version>1.0-SNAPSHOTversion>
<properties>
<maven.compiler.source>8maven.compiler.source>
<maven.compiler.target>8maven.compiler.target>
properties>
<dependencies>
<dependency>
<groupId>org.springframeworkgroupId>
<artifactId>spring-contextartifactId>
<version>5.1.7.RELEASEversion>
dependency>
<dependency>
<groupId>org.springframework.amqpgroupId>
<artifactId>spring-rabbitartifactId>
<version>2.1.8.RELEASEversion>
dependency>
<dependency>
<groupId>junitgroupId>
<artifactId>junitartifactId>
<version>4.12version>
dependency>
<dependency>
<groupId>org.springframeworkgroupId>
<artifactId>spring-testartifactId>
<version>5.1.7.RELEASEversion>
dependency>
dependencies>
<build>
<plugins>
<plugin>
<groupId>org.apache.maven.pluginsgroupId>
<artifactId>maven-compiler-pluginartifactId>
<version>3.8.0version>
<configuration>
<source>1.8source>
<target>1.8target>
configuration>
plugin>
plugins>
build>
project>
rabbitmq.host=172.16.98.133
rabbitmq.port=5672
rabbitmq.username=heima
rabbitmq.password=heima
rabbitmq.virtual-host=/itcast
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:context="http://www.springframework.org/schema/context"
xmlns:rabbit="http://www.springframework.org/schema/rabbit"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/context
https://www.springframework.org/schema/context/spring-context.xsd
http://www.springframework.org/schema/rabbit
http://www.springframework.org/schema/rabbit/spring-rabbit.xsd">
<context:property-placeholder location="classpath:rabbitmq.properties"/>
<rabbit:connection-factory id="connectionFactory" host="${rabbitmq.host}"
port="${rabbitmq.port}"
username="${rabbitmq.username}"
password="${rabbitmq.password}"
virtual-host="${rabbitmq.virtual-host}"/>
<rabbit:admin connection-factory="connectionFactory"/>
<rabbit:queue id="spring_queue" name="spring_queue" auto-declare="true"/>
<rabbit:queue id="spring_fanout_queue_1" name="spring_fanout_queue_1" auto-declare="true"/>
<rabbit:queue id="spring_fanout_queue_2" name="spring_fanout_queue_2" auto-declare="true"/>
<rabbit:fanout-exchange id="spring_fanout_exchange" name="spring_fanout_exchange" auto-declare="true">
<rabbit:bindings>
<rabbit:binding queue="spring_fanout_queue_1"/>
<rabbit:binding queue="spring_fanout_queue_2"/>
rabbit:bindings>
rabbit:fanout-exchange>
<rabbit:queue id="spring_topic_queue_star" name="spring_topic_queue_star" auto-declare="true"/>
<rabbit:queue id="spring_topic_queue_well" name="spring_topic_queue_well" auto-declare="true"/>
<rabbit:queue id="spring_topic_queue_well2" name="spring_topic_queue_well2" auto-declare="true"/>
<rabbit:topic-exchange id="spring_topic_exchange" name="spring_topic_exchange" auto-declare="true">
<rabbit:bindings>
<rabbit:binding pattern="heima.*" queue="spring_topic_queue_star"/>
<rabbit:binding pattern="heima.#" queue="spring_topic_queue_well"/>
<rabbit:binding pattern="itcast.#" queue="spring_topic_queue_well2"/>
rabbit:bindings>
rabbit:topic-exchange>
<rabbit:template id="rabbitTemplate" connection-factory="connectionFactory"/>
beans>
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.amqp.rabbit.core.RabbitTemplate;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(locations = "classpath:spring-rabbitmq-producer.xml")
public class ProducerTest {
//1、注入RabbitTemplate
@Autowired
private RabbitTemplate rabbitTemplate;
@Test
public void testHelloWorld(){
//2、发送消息
rabbitTemplate.convertAndSend("spring_queue","hello world spring....");
}
@Test
public void testFanout(){
//2、发送消息
rabbitTemplate.convertAndSend("spring_fanout_exchange","","spring fanout");
}
public void testTopic(){
//2、发送消息
rabbitTemplate.convertAndSend("spring_topic_exchange","heima.hehe.haha","spring topics");
}
}
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0modelVersion>
<groupId>org.examplegroupId>
<artifactId>spring-rabbitmq-consumerartifactId>
<version>1.0-SNAPSHOTversion>
<properties>
<maven.compiler.source>8maven.compiler.source>
<maven.compiler.target>8maven.compiler.target>
properties>
<dependencies>
<dependency>
<groupId>org.springframeworkgroupId>
<artifactId>spring-contextartifactId>
<version>5.1.7.RELEASEversion>
dependency>
<dependency>
<groupId>org.springframework.amqpgroupId>
<artifactId>spring-rabbitartifactId>
<version>2.1.8.RELEASEversion>
dependency>
<dependency>
<groupId>junitgroupId>
<artifactId>junitartifactId>
<version>4.12version>
dependency>
<dependency>
<groupId>org.springframeworkgroupId>
<artifactId>spring-testartifactId>
<version>5.1.7.RELEASEversion>
dependency>
dependencies>
<build>
<plugins>
<plugin>
<groupId>org.apache.maven.pluginsgroupId>
<artifactId>maven-compiler-pluginartifactId>
<version>3.8.0version>
<configuration>
<source>1.8source>
<target>1.8target>
configuration>
plugin>
plugins>
build>
project>
rabbitmq.host=172.16.98.133
rabbitmq.port=5672
rabbitmq.username=heima
rabbitmq.password=heima
rabbitmq.virtual-host=/itcast
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:context="http://www.springframework.org/schema/context"
xmlns:rabbit="http://www.springframework.org/schema/rabbit"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/context
https://www.springframework.org/schema/context/spring-context.xsd
http://www.springframework.org/schema/rabbit
http://www.springframework.org/schema/rabbit/spring-rabbit.xsd">
<context:property-placeholder location="classpath:rabbitmq.properties"/>
<rabbit:connection-factory id="connectionFactory" host="${rabbitmq.host}"
port="${rabbitmq.port}"
username="${rabbitmq.username}"
password="${rabbitmq.password}"
virtual-host="${rabbitmq.virtual-host}"/>
<bean id="springQueueListener" class="com.itheima.rabbitmq.listener.SpringQueueListener"/>
<rabbit:listener-container connection-factory="connectionFactory" auto-declare="true">
<rabbit:listener ref="springQueueListener" queue-names="spring_queue"/>
rabbit:listener-container>
beans>
package com.itheima.rabbitmq.listener;
import org.springframework.amqp.core.Message;
import org.springframework.amqp.core.MessageListener;
public class SpringQueueListener implements MessageListener {
@Override
public void onMessage(Message message) {
//打印消息
System.out.println(new String(message.getBody()));
}
}
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(locations = "classpath:spring-rabbitmq-consumer.xml")
public class ConsumerTest {
@Test
public void Test1(){
boolean flag = true;
while(flag){
}
}
}
4.0.0
org.example
producer-springboot
1.0-SNAPSHOT
org.springframework.boot
spring-boot-starter-parent
2.1.4.RELEASE
8
8
org.springframework.boot
spring-boot-starter-amqp
org.springframework.boot
spring-boot-starter-test
# 配置RabbitMQ的基本信息 ip、端口、username password
spring:
rabbitmq:
host: 172.16.98.133 # ip
port: 5672
username: heima
password: heima
virtual-host: /
RabbitMQConfig.java
package com.itheima.rabbitmq.config;
import org.springframework.amqp.core.*;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
@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 bootQueueExchange(@Qualifier("bootQueue") Queue queue, @Qualifier("bootExchange") Exchange exchange){
return BindingBuilder.bind(queue).to(exchange).with("boot.#").noargs();
}
}
ProducerTest.java
import com.itheima.rabbitmq.config.RabbitMQConfig;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.amqp.rabbit.core.RabbitTemplate;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.test.context.junit4.SpringRunner;
@SpringBootTest
@RunWith(SpringRunner.class)
public class ProducerTest {
//1、注入rabbitTemplate
@Autowired
private RabbitTemplate rabbitTemplate;
@Test
public void testSend(){
rabbitTemplate.convertAndSend(RabbitMQConfig.EXCHANGE_NAME,"boot.haaha","这是一条发给topics模式的消息!");
}
}
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0modelVersion>
<parent>
<groupId>org.springframework.bootgroupId>
<artifactId>spring-boot-starter-parentartifactId>
<version>2.1.4.RELEASEversion>
<relativePath/>
parent>
<groupId>com.examplegroupId>
<artifactId>consumer-springbootartifactId>
<version>0.0.1-SNAPSHOTversion>
<name>consumer-springbootname>
<description>Demo project for Spring Bootdescription>
<properties>
<java.version>1.8java.version>
properties>
<dependencies>
<dependency>
<groupId>org.springframework.bootgroupId>
<artifactId>spring-boot-starter-amqpartifactId>
dependency>
<dependency>
<groupId>org.springframework.bootgroupId>
<artifactId>spring-boot-starter-testartifactId>
dependency>
dependencies>
project>
# 配置RabbitMQ的基本信息 ip、端口、username password
spring:
rabbitmq:
host: 172.16.98.133 # ip
port: 5672
username: heima
password: heima
virtual-host: /
package com.example.consumerspringboot;
import org.springframework.amqp.core.Message;
import org.springframework.amqp.rabbit.annotation.RabbitListener;
import org.springframework.stereotype.Component;
@Component
public class RabbitMQListener {
@RabbitListener(queues = "boot_queue")
public void ListenerQueue(Message message){
System.out.println(new String(message.getBody()));
}
}