1. RabbitMQ配置
1.1RabbitMQ管理命令行
# 1.服务器启动相关命令行
systemctl start|restart|stop|status rabbitmq-server
# 2.管理命令行
rabbitmqctl help #查看更多命令
# 3.插件管理命令行
rabbitmq-plugins enable|disable|list
1.2 Web管理界面介绍
1.2.1 OverView概览
2. 消息队列模式
第一种模型(直连)
在上图所示的模型中有以下概念
- P:生产者
- C:消费者
- queue:消息队列
1.开发生产者
connection = RabbitmqUtils.getConnection();
channel = connection.createChannel();
/**
* 参数一:队列名字(不存在会自动创建)、参数二:是否要持久化、
* 参数三:是否独占队列、参数四:是否自动删除队列
* 参数五:额外参数
*/
channel.queueDeclare("hello", false, false, false, null);
/**
* 参数一:交换机名称、参数二:队列名称、
* 参数三:额外消息设置、参数四:消息具体内容
*/
channel.basicPublish("", "hello", false, false, null, "hello rabbitmq".getBytes());
2.消费者
Connection connection = RabbitMqUtils.getConnection();
Channel channel = connection.createChannel();
//通道绑定队列
channel.queueDeclare("hello",false,false,false,null);
/**
* 参数一:队列名称、参数二:消息自动确认
*/
channel.basicConsume("hello",true,new DefaultConsumer(channel){
@Override
public void handleDelivery(String consumerTag, Envelope envelope, AMQP.BasicProperties properties, byte[] body) throws IOException {
System.out.println(new String(body));
}
});
工具类RabbitMQUtils.java
package com.rabbitmq.study;
import com.rabbitmq.client.Channel;
import com.rabbitmq.client.Connection;
import com.rabbitmq.client.ConnectionFactory;
import java.io.IOException;
import java.util.concurrent.TimeoutException;
/**
* @FileName: RabbitMQUtils
* @Author Steven
* @Date: 2020/11/14
*/
public class RabbitMQUtils {
private static ConnectionFactory factory;
static {
factory = new ConnectionFactory();
factory.setHost("xxx.xxx.xxx.xxx");
factory.setPort(5672);
factory.setVirtualHost("/cms");
//这只访问虚拟主机的用户名、密码
factory.setUsername("ems");
factory.setPassword("123");
}
public static Connection getConnection() {
try {
return factory.newConnection();
} catch (IOException e) {
e.printStackTrace();
} catch (TimeoutException e) {
e.printStackTrace();
}
return null;
}
public static void closeChannelAndConn(Channel channel, Connection conn) {
try {
if (channel != null) {
channel.close();
}
if (conn != null) {
conn.close();
}
} catch (Exception e) {
e.printStackTrace();
}
}
}
3.参数说明
/**
* 参数一:队列名字(不存在会自动创建)、参数二:是否要持久化、
* 参数三:是否独占队列、参数四:是否自动删除队列
* 参数五:额外参数
*/
channel.queueDeclare("aa", true, false, false, null);
第二种模型(work queue)
work queue,也被称为(task queue),任务模型,多个消费者绑定一个队列共同消费,队列中的消息一旦被消费就会消失,因此消息不回被重复消费
角色:
- P:生产者,任务发布
- queue:队列存放消息
- C1:消费者1,消费消息较慢
- C2:消费者2,消费消息较快
1.生产者Send.java
Connection connection = RabbitMQUtils.getConnection();
Channel channel = connection.createChannel();
/**
* 参数一:队列名字(不存在会自动创建)、参数二:是否要持久化、
* 参数三:是否独占队列、参数四:是否自动删除队列
* 参数五:额外参数
*/
channel.queueDeclare("work", true, false, false, null);
/**
* 参数一:交换机名称、参数二:队列名称、
* 参数三:额外消息设置、参数四:消息具体内容
*/
for (int i = 1; i <=100 ; i++) {
channel.basicPublish("", "work", false, false, MessageProperties.PERSISTENT_TEXT_PLAIN, (i+"hello rabbitmq").getBytes());
}
RabbitMQUtils.closeChannelAndConn(channel,connection);
2.消费者:Recv1.java
Connection connection = RabbitMQUtils.getConnection();
Channel channel = connection.createChannel();
channel.queueDeclare("work",true,false,false,null);
channel.basicConsume("work",true,new DefaultConsumer(channel){
@Override
public void handleDelivery(String consumerTag, Envelope envelope, AMQP.BasicProperties properties, byte[] body) throws IOException {
System.out.println("消费者1:"+new String(body));
try {
TimeUnit.SECONDS.sleep(2);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
});
3.消费者:Recv2.java
Connection connection = RabbitMQUtils.getConnection();
Channel channel = connection.createChannel();
channel.queueDeclare("work",true,false,false,null);
channel.basicConsume("work",true,new DefaultConsumer(channel){
@Override
public void handleDelivery(String consumerTag, Envelope envelope, AMQP.BasicProperties properties, byte[] body) throws IOException {
System.out.println("消费者2:"+new String(body));
try {
TimeUnit.SECONDS.sleep(2);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
});
消息确认机制
1.消费者:Recv1.java
Connection connection = RabbitMQUtils.getConnection();
Channel channel = connection.createChannel();
//每次只能消费一个消息
channel.basicQos(1);
channel.queueDeclare("work", true, false, false, null);
channel.basicConsume("work", false, new DefaultConsumer(channel) {
@Override
public void handleDelivery(String consumerTag, Envelope envelope, AMQP.BasicProperties properties, byte[] body) throws IOException {
System.out.println("消费者2:" + new String(body));
//参数1:确认具体为队列中的那个消息,参数2:是否同时确认多个消息
channel.basicAck(envelope.getDeliveryTag(),false);
try {
TimeUnit.SECONDS.sleep(2);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
});
2.消费者:Recv2.java
Connection connection = RabbitMQUtils.getConnection();
Channel channel = connection.createChannel();
//每次只能消费一个消息
channel.basicQos(1);
channel.queueDeclare("work",true,false,false,null);
channel.basicConsume("work",false,new DefaultConsumer(channel){
@Override
public void handleDelivery(String consumerTag, Envelope envelope, AMQP.BasicProperties properties, byte[] body) throws IOException {
System.out.println("消费者2:"+new String(body));
//参数1:确认具体为队列中的那个消息,参数2:是否同时确认多个消息
channel.basicAck(envelope.getDeliveryTag(),false);
}
});
第三种模型(fanout)
fanout:扇出,又称为广播
在广播模式下,消息发送流程
- 多个消费者
- 每个消费者都绑定这自己的队列(queue)
- 每个队列都要绑定到交换机(exchange)上
- 生产者只将消息发送给交换机
- 交换机(exchange)决定将消息发送给哪个队列
1.开发生产者:send.java
Connection connection = RabbitMQUtils.getConnection();
Channel channel = connection.createChannel();
/**
* 通道声明为交换机
* 参数1:交换机名称、参数2:交换机类型 fanout:代表广播类型
*/
channel.exchangeDeclare("logs","fanout");
channel.basicPublish("logs","",null,"RabbitMq fanout".getBytes());
2.消费者:Recv.java
channel.exchangeDeclare("logs","fanout");
//创建临时队列
String queueName = channel.queueDeclare().getQueue();
channel.queueBind(queueName,"logs","");
channel.basicConsume(queueName,true,new DefaultConsumer(channel){
@Override
public void handleDelivery(String consumerTag, Envelope envelope, AMQP.BasicProperties properties, byte[] body) throws IOException {
System.out.println("消费者1:"+new String(body));
}
});
第四种模型(Routing)
1.Routing之订阅模型-Direct(直连)
在fanout模式中,一条消息,会被所有订阅的消费者消费,但在某些情景下,我们希望不同的消息被不同的消费者消费,这时就用到Direct类型的Exchange。
在Direct模型下:
- 队列与交换机绑定,不能任意绑定,而是指定一个RoutingKey(路由key)
- 生产者向Exchange(交换机)发送消息时需要指定RoutingKey(路由key)
- Exchange不在把消息交给绑定的队列,而是根据消息的Routing Key进行判断,只有当消息中的Routing Key队列中的Routing Key完全一致才会收到消息
流程
图解
- P:生产者,向exchange发送消息时会指定一个Routing Key
- X:Exchange(交换机),接受生产者消息,然后把消息发送给与Routing Key相同的队列
- C1:消费者,指定需要接受Routing Key为error的消息
- C2:消费者,指定需要接受Routing key为error、info、warning的消息
1.生产者
Connection connection = RabbitMQUtils.getConnection();
Channel channel = connection.createChannel();
channel.exchangeDeclare("direct.logs", "direct");
String queueName = channel.queueDeclare().getQueue();
String routingKey = "error";
channel.queueBind(queueName, "direct.logs", routingKey);
channel.basicPublish("direct.logs", routingKey, null, (routingKey + " Rabbit Direct").getBytes("utf-8"));
RabbitMQUtils.closeChannelAndConn(channel, connection);
2.消费者-1
channel.exchangeDeclare("direct.logs", "direct");
String queueName = channel.queueDeclare().getQueue();
channel.queueBind(queueName, "direct.logs", "info");
channel.queueBind(queueName, "direct.logs", "warning");
channel.queueBind(queueName, "direct.logs", "error");
channel.basicConsume(queueName, true, new DefaultConsumer(channel) {
@Override
public void handleDelivery(String consumerTag, Envelope envelope, AMQP.BasicProperties properties, byte[] body) throws IOException {
System.out.println("Recv1日志:" + new String(body));
}
});
3.消费者-2
Connection connection = RabbitMQUtils.getConnection();
Channel channel = connection.createChannel();
/**
* 参数一:交换机名称、参数二:交换机的类型:(direct)直连式
*/
channel.exchangeDeclare("direct.logs", "direct");
/**
* 声明一个临时队列
*/
String queueName = channel.queueDeclare().getQueue();
/**
* 参数一:队列名称、参数二:交换机名称、参数三:RoutingKey(路由key)
*/
channel.queueBind(queueName, "direct.logs", "info");
channel.queueBind(queueName, "direct.logs", "warning");
channel.queueBind(queueName, "direct.logs", "error");
channel.basicConsume(queueName, true, new DefaultConsumer(channel) {
@Override
public void handleDelivery(String consumerTag, Envelope envelope, AMQP.BasicProperties properties, byte[] body) throws IOException {
System.out.println("Recv1日志:" + new String(body));
}
});
2.Routing之订阅模型-Topic(直连)
topic类型的exchange与direct相比,都可以根据RoutingKey把消息路由到不同的队列中,只不过topic在绑定队列时RoutingKey可以使用通配符,这种模型RoutingKey一般有一个或多个单词组成多个单词间用“.”分割类如:item.insert
# 通配符
*(start):匹配一个单词
#(start):匹配多个单词
#如:
hello.*: hello.java、hello.world
hello.#: hello.java.world
1.生产者:send.java
Connection connection = RabbitMQUtils.getConnection();
Channel channel = connection.createChannel();
// 参数一:交换机名称
channel.exchangeDeclare("topics","topic");
String routingKey="user.save.findAll";
channel.basicPublish("topics",routingKey,null,("RabbitMQ Topic RoutingKey:"+routingKey).getBytes());
RabbitMQUtils.closeChannelAndConn(channel,connection);
2.消费者:Recv.java
Connection connection = RabbitMQUtils.getConnection();
Channel channel = connection.createChannel();
channel.exchangeDeclare("topics","topic");
String queueName = channel.queueDeclare().getQueue();
String routingKey="user.#";
channel.queueBind(queueName,"topics",routingKey);
channel.basicConsume(queueName,true,new DefaultConsumer(channel){
@Override
public void handleDelivery(String consumerTag, Envelope envelope, AMQP.BasicProperties properties, byte[] body) throws IOException {
System.out.println("RabbitMQ tutorials Topic:"+new String(body));
}
});
3.SpringBoot整合RabbitMQ
3.1 初始环境单间
1.引入依赖
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-amqp</artifactId>
</dependency>
2.在application.yml中添加相关配置
server:
address: 8080
spring:
rabbitmq:
addresses: 47.101.36.177
username: ems
password: 123
virtual-host: /cms
port: 5672
application:
name: rabbitmq-study
RabbitMQ简化操作对象,使用时直接在项目中注入即可
3.2 第一种Hello World模型使用
1.生产者:Send.java
//注入RabbitMQ模板对象
@Autowired
private RabbitTemplate rabbitTemplate;
@Test
public void testHelloWord() {
rabbitTemplate.convertAndSend("hello","hello world");
}
2.消费者:HelloConsumer.java
@Component
// 持久化,非独占、非自动删除的队列
@RabbitListener(queuesToDeclare = @Queue(value = "hello",durable = "false",autoDelete = "false"))
public class HelloConsumer {
@RabbitHandler
public void recive(String message){
System.out.println("消息队列中的消息:"+message);
}
}
3.3 第二种work queue模型使用
1.生产者:Send.java
@Test
public void testWork(){
for (int i = 0; i < 10; i++) {
rabbitTemplate.convertAndSend("work","Work模型");
}
}
2.消费者:WorkConsumer.java
@Component
public class WorkConsumer {
@RabbitListener(queuesToDeclare=@Queue("work"))
public void receive1(String message){
System.out.println("work model1:"+message);
}
@RabbitListener(queuesToDeclare=@Queue("work"))
public void receive2(String message){
System.out.println("work model2:"+message);
}
}
3.4第三种广播模型(fanout)使用
1.生产者:send.java
/**
* 广播模型fanout
*/
@Test
public void fanoutModel(){
rabbitTemplate.convertAndSend("logs","","fanout广播模型");
}
2.消费者:FanoutConsumer
@Component
public class FanCoutModel {
@RabbitListener(bindings = {
@QueueBinding(value = @Queue,//不指定时,创建临时队列
exchange =@Exchange(name = "logs",type = "fanout"))
})
public void receive1(String message){
System.out.println("message1:"+message);
}
@RabbitListener(bindings = {
@QueueBinding(value = @Queue,//不指定时,创建临时队列
exchange =@Exchange(name = "logs",type = "fanout"))
})
public void receive2(String message){
System.out.println("message1:"+message);
}
}
3.5第三种路由(RoutingKey)模型使用
1.生产者:send.java
/**
* route模式
*/
@Test
public void testRoute(){
rabbitTemplate.convertAndSend("directs","error","通过路由发送error信息");
}
2.消费者:RouteConsumer.java
@Component
public class RouteConsumer {
@RabbitListener(bindings = {
@QueueBinding(value = @Queue,exchange = @Exchange(name = "directs",type = "direct"),
key = {"info","error","warning"})
})
public void receive1(String message){
System.out.println("receive1:"+message);
}
@RabbitListener(bindings = {
@QueueBinding(value = @Queue,exchange = @Exchange(name = "directs",type = "direct"),
key ={"error"})
})
public void receive2(String message){
System.out.println("receive2:"+message);
}
}
3.6第三种动态路由模型(Topic)使用
1.生产者:send.java
/**
* topic模式(动态路由)
*/
@Test
public void testTopic(){
rabbitTemplate.convertAndSend("topics","user.save","topic动态路由模式");
}
2.消费者:TopicConsumer.java
@Component
public class TopicConsumer {
@RabbitListener(bindings = {
@QueueBinding(value = @Queue,exchange = @Exchange(
value = "topics",type = "topic"
),key = {"user.*"})
})
public void receive1(String message){
System.out.println("receive1:"+message);
}
@RabbitListener(bindings = {@QueueBinding(value = @Queue,exchange = @Exchange(
name = "topics",type = "topic"),key = {"user.#"})})
public void receive2(String message){
System.out.println("receive2:"+message);
}
}