RabbitMQ 是一个开源的消息代理(message broker)系统,最初由 Rabbit Technologies Ltd 开发,并在开源社区的支持下不断发展和完善。它提供了强大的消息传递机制,被广泛用于构建分布式系统和应用。本博客将深入探讨 RabbitMQ 的各个方面,以便读者更全面地了解和使用这一重要的消息代理系统。
RabbitMQ 是一种高性能、开源、跨平台的消息代理系统,它实现了高效的消息传递机制,允许不同的应用程序通过消息进行通信。RabbitMQ 支持多种消息传递模式,包括点对点、发布/订阅和路由等,使其成为构建分布式系统和微服务架构的理想选择。
消息队列是一种通信方式,允许不同的应用程序或服务之间通过异步消息传递进行通信。它们通常包括生产者、消息队列和消费者。生产者负责创建和发送消息,消息队列负责存储消息,消费者负责接收并处理消息。
3.1. 队列(Queues)
队列是 RabbitMQ 存储消息的地方。生产者将消息发送到队列,消费者从队列中接收消息。队列可以看作是消息的缓冲区,确保消息能够按顺序传递。
3.2. 交换机(Exchanges)
交换机是消息的分发中心,负责将消息路由到一个或多个队列。RabbitMQ 提供了不同类型的交换机,如直接交换机、扇出交换机、主题交换机等,以支持不同的消息传递模式。
3.3. 绑定(Bindings)
绑定定义了交换机如何将消息路由到队列。它将交换机与队列之间建立关联关系,确保消息能够被正确地路由到目标队列。
在 RabbitMQ 中,生产者使用 Exchange 将消息发布到队列,而消费者从队列中获取消息并进行处理。这个过程包括消息的发布、路由和消费。
为了确保消息不会丢失,RabbitMQ 提供了消息确认机制和消息持久化选项。消息确认确保消息被成功处理,而消息持久化则将消息存储在磁盘上,以便在代理重启时不会丢失。
RabbitMQ消息投递的路径为:
生产者 —> 交换机 —> 队列 —> 消费者
在RabbitMQ工作的过程中,每个环节消息都可能传递失败,那么RabbitMQ是如何监听消息是否成功投递的呢?有三种可靠性投的机制:
spring:
rabbitmq:
host: 192.168.0.162
port: 5672
username: cvdf
password: cvdf
virtual-host: /cvdf
# 开启确认模式
publisher-confirm-type: correlated
spring:
rabbitmq:
host: 192.168.184.136
port: 5672
virtual-host: /cvdf
username: cvdf
password: cvdf
# 开启确认模式
publisher-confirm-type: correlated
# 开启回退模式
publisher-returns: true
在RabbitMQ中,消费者接收到消息后会向队列发送确认签收的消息,只有确认签收的消息才会被移除队列。这种机制称为消费者消息确认(Consumer Acknowledge,简称Ack)。类似快递员派送快递也需要我们签收,否则一直存在于快递公司的系统中。
消息分为自动确认和手动确认。自动确认指消息只要被消费者接收到,无论是否成功处理消息,则自动签收,并将消息从队列中移除。但是在实际开发中,收到消息后可能业务处理出现异常,那么消息就会丢失。此时需要设置手动签收,即在业务处理成功再通知签收消息,如果出现异常,则拒签消息,让消息依然保留在队列当中。
1. 自动确认:spring.rabbitmq.listener.simple.acknowledge=“none”
2. 手动确认:spring.rabbitmq.listener.simple.acknowledge="manual
spring:
rabbitmq:
host: 192.168.184.136
port: 5672
virtual-host: /cvdf
username: cvdf
password: cvdf
# 手动签收消息
listener:
simple:
acknowledge-mode: manual
RabbitMQ 支持多种消息传递模式,包括简单队列模式、发布/订阅模式和路由模式。每种模式都适用于不同的应用场景,允许灵活的消息传递。
6.1. 简单队列模式
简单队列模式是最基本的模式,将消息发送到一个队列,一个消费者从队列中获取消息并处理。适用于点对点通信。
特点:
/**
* 简单模式生产者
*/
public class Producer {
@SneakyThrows
public static void main(String[] args) {
//1.创建链接工厂
ConnectionFactory connectionFactory = new ConnectionFactory();
connectionFactory.setHost("192.168.184.136");
connectionFactory.setPort(5672);
connectionFactory.setUsername("cvdf");
connectionFactory.setPassword("cvdf");
connectionFactory.setVirtualHost("/cvdf");
//2.创建链接
Connection connection = connectionFactory.newConnection();
//3.创建信道
Channel channel = connection.createChannel();
//4.创建队列,如果队列存在,则使用
/**
* 参数1:队列名
* 参数2:是否持久化,true表示MQ重启后队列还在
* 参数3:是否私有化,false表示所有消费者都可以访问,true表示只有第一次拥有它的消费者才能访问
* 参数4:是否自动删除,true表示不在使用队列时自动删除队列
* 参数5:其他额外参数
*/
channel.queueDeclare("simple_queue",false,false,false,null);
//5.发送消息
/**
* 参数1:交换机名 ""表示默认交换机
* 参数2:路由键,简单模式就是队列名
* 参数3:其他额外参数
* 参数4:要传递的消息字节数组
*/
String message = "hello simple message mq!!!";
channel.basicPublish("","simple_queue",null,message.getBytes());
//6.关闭信道和连接
channel.close();
connection.close();
System.out.println("发送成功");
}
}
6.2. 发布/订阅模式
发布/订阅模式允许多个消费者订阅同一个队列,并同时接收消息。适用于广播通信。
在开发过程中,有一些消息需要不同消费者进行不同的处理,如电商网站的同一条促销信息需要短信发送、邮件发送、站内信发送等。此时可以使用发布订阅模式(Publish/Subscribe)
特点:
/**
* 发布订阅生产者
*/
public class Producer {
@SneakyThrows
public static void main(String[] args) {
//1.创建链接工厂
ConnectionFactory connectionFactory = new ConnectionFactory();
connectionFactory.setHost("192.168.184.136");
connectionFactory.setPort(5672);
connectionFactory.setUsername("cvdf");
connectionFactory.setPassword("cvdf");
connectionFactory.setVirtualHost("/cvdf");
//2.创建链接
Connection connection = connectionFactory.newConnection();
//3.创建信道
Channel channel = connection.createChannel();
//4.创建交换机
/**
* 参数1:交换机名
* 参数2:交换机类型
* 参数3:交换机持久化
*/
channel.exchangeDeclare("exchange_fanout", BuiltinExchangeType.FANOUT,true);
//5.创建队列,如果队列存在,则使用
/**
* 参数1:队列名
* 参数2:是否持久化,true表示MQ重启后队列还在
* 参数3:是否私有化,false表示所有消费者都可以访问,true表示只有第一次拥有它的消费者才能访问
* 参数4:是否自动删除,true表示不在使用队列时自动删除队列
* 参数5:其他额外参数
*/
channel.queueDeclare("send_email",true,false,false,null);
channel.queueDeclare("send_message",true,false,false,null);
channel.queueDeclare("send_station",true,false,false,null);
//6.交换机绑定队列
/**
* 参数1:队列名
* 参数2:交换机名
* 参数3:路由关键字 ,发布订阅模式写""即可
*/
channel.queueBind("send_email","exchange_fanout","");
channel.queueBind("send_message","exchange_fanout","");
channel.queueBind("send_station","exchange_fanout","");
//7.发送消息
/**
* 参数1:交换机名 ""表示默认交换机
* 参数2:路由键,简单模式就是队列名
* 参数3:其他额外参数 ,这里表示消息为持久化消息,既除了保存内存还会保存到磁盘中
* 参数4:要传递的消息字节数组
*/
for (int i = 1; i <=10 ; i++) {
channel.basicPublish("exchange_fanout",
"",
null,
("你好,尊敬的用户,秒杀商品开抢了"+i).getBytes());
}
//8.关闭信道和连接
channel.close();
connection.close();
System.out.println("发送成功");
}
}
6.3. 路由模式
路由模式通过交换机和绑定来控制消息的路由方式,允许根据消息的特性将消息发送到不同的队列。适用于灵活的消息分发。使用发布订阅模式时,所有消息都会发送到绑定的队列中,但很多时候,不是所有消息都无差别的发布到所有队列中。比如电商网站的促销活动,双十一大促可能会发布到所有队列;而一些小的促销活动为了节约成本,只发布到站内信队列。此时需要使用路由模式(Routing)完成这一需求。
特点:
/**
* 路由模式:消息生产者
*/
public class
{
@SneakyThrows
public static void main(String[] args) {
//1.创建连接工厂
ConnectionFactory factory = new ConnectionFactory();
factory.setHost("192.168.184.136");
factory.setPort(5672);
factory.setVirtualHost("/cvdf");
factory.setUsername("cvdf");
factory.setPassword("cvdf");
//2.创建连接
Connection connection = factory.newConnection();
//3.创建信道
Channel channel = connection.createChannel();
//4.声明交换机
/**
* 参数1:交换机名
* 参数2:交换机类型
* 参数3:是否持久化
*/
channel.exchangeDeclare("exchange_direct", BuiltinExchangeType.DIRECT,true);
//5.声明队列
/**
* 参数1:队列名
* 参数2:吃否持久化
* 参数3:是否私有
* 参数4: 是否自动删除
* 参数5:其他参数
*/
channel.queueDeclare("routing_queue1",true,false,false,null);
channel.queueDeclare("routing_queue2",true,false,false,null);
//6.绑定routKey
/**
* 参数1:队列名
* 参数2:交换机名
* 参数3:routeKey 路由模式routeKey必须指定,只能指定单词,多个单词用.隔离
*/
channel.queueBind("routing_queue1","exchange_direct","info");
channel.queueBind("routing_queue2","exchange_direct","info");
channel.queueBind("routing_queue2","exchange_direct","error");
//7.发送消息
/**
* 参数1:交换机名
* 参数2:路由键,发送到指定路由key
* 参数3:其他额外参数 ,这里表示消息为持久化消息,既除了保存内存还会保存到磁盘中
* 参数4:要传递的消息字节数组
*/
channel.basicPublish("exchange_direct","info",null,"info消息".getBytes());
channel.basicPublish("exchange_direct","error",null,"error消息".getBytes());
//8.关闭信道和连接
channel.close();
connection.close();
System.out.println("发送成功");
}
}
通配符模式(Topic)是在路由模式的基础上,给队列绑定带通配符的路由关键字,只要消息的RoutingKey能实现通配符匹配,就会将消息转发到该队列。通配符模式比路由模式更灵活,使用topic交换机。
通配符规则:
/**
* 通配符模式:消息生产者
*/
public class TopicProducer {
@SneakyThrows
public static void main(String[] args) {
//1.创建连接工厂
ConnectionFactory factory = new ConnectionFactory();
factory.setHost("192.168.184.136");
factory.setPort(5672);
factory.setVirtualHost("/cvdf");
factory.setUsername("cvdf");
factory.setPassword("cvdf");
//2.创建连接
Connection connection = factory.newConnection();
//3.创建信道
Channel channel = connection.createChannel();
//4.声明交换机:交换机名字、类型、是否持久化
channel.exchangeDeclare("exchange_topic", BuiltinExchangeType.TOPIC,true);
//5.声明队列:队列名,是否持久化,是否私有,是否自动删除,额外信息
channel.queueDeclare("topic_queue1",true,false,false,null);
channel.queueDeclare("topic_queue2",true,false,false,null);
//6.绑定关系
channel.queueBind("topic_queue1","exchange_topic","*.goods.*");
channel.queueBind("topic_queue1","exchange_topic","*.adv.*");
channel.queueBind("topic_queue2","exchange_topic","#.goods.#");
//7.发送消息
channel.basicPublish("exchange_topic","huawei.goods.phone",null,"华为手机商品信息".getBytes());
channel.basicPublish("exchange_topic","huawei.adv.phone",null,"华为手机广告信息".getBytes());
//8.关闭资源
channel.close();
connection.close();
}
}
以上四种生产者对应的消费者都可以由以下消费者改造,不在重复编写
#消费者代码
public class Consumer {
@SneakyThrows
public static void main(String[] args) {
//1.创建链接工厂
ConnectionFactory connectionFactory = new ConnectionFactory();
connectionFactory.setHost("192.168.184.136");
connectionFactory.setPort(5672);
connectionFactory.setUsername("psjj");
connectionFactory.setPassword("psjj");
connectionFactory.setVirtualHost("/psjj");
//2.创建链接
Connection connection = connectionFactory.newConnection();
//3.创建信道
Channel channel = connection.createChannel();
//4.监听队列
/**
* 参数1:监听的队列名
* 参数2:是否自动签收,如果设置为false,则需要手动确认消息已接收,否则MQ会一直发送消息
* 参数3:Consumer的实现类,重写该类方法表示接受到消息后如何消费
*/
channel.basicConsume("simple_queue", true, new DefaultConsumer(channel) {
@Override
public void handleDelivery(String consumerTag, Envelope envelope, AMQP.BasicProperties properties, byte[] body) throws IOException {
String message = new String(body,"utf-8");
System.out.println("接收消息,消息为:"+message);
}
});
}
}
以上就是RabbitMQ的基础概念、核心特性的知识总结,RabbitMQ 的高级特性、应用场景、集成与安全性等方面由下一篇RabbitMQ 的高级特性陈述,如有需要请移步下一篇文章。
以上就是全部内容,如果你有任何问题、意见或建议,都欢迎在评论中分享。让我们继续分享知识,共同成长,一起走向更加美好的未来。感谢你们的阅读,祝愿你们在未来的道路上一帆风顺!