最近心血来潮,突然对RibbitMQ来了兴趣,其实在很早以前,我就大致了解过mq的工作原理,但是一直自从毕业工作到现在,一直都没有在实际的业务场景中使用过,所以搁置了好久,最近正好有空抽出了些时间,于是决定学习一波并记录下来,方便日后有需要的时候拿出来看看,同时也给社区的小伙伴们提供一些参考,如有错误的地方,还请指正出来,谢谢!
首先,学习RibbitMQ,我们得先知道什么是RibbitMQ:
RabbitMQ 是一个开源的消息代理软件,也被称为消息队列系统。它的主要功能是实现不同系统之间的通信,通过发送和接收消息来解耦和异步化应用程序。RabbitMQ 使用高级消息队列协议(AMQP,Advanced Message Queuing Protocol)作为其核心协议。
RibbitMQ的核心概念
- 生产者(Producer):
负责发送消息的应用或服务。生产者将消息发送到 RabbitMQ。 - 消费者(Consumer):
负责接收和处理消息的应用或服务。消费者从 RabbitMQ 中获取消息。 - 队列(Queue):
消息的存储地点。生产者将消息发送到队列,消费者从队列中读取消息。 - 交换机(Exchange):
生产者将消息发送到交换机,交换机根据绑定规则(Routing Rules)决定将消息发送到哪个队列。 - 绑定(Binding):
交换机和队列之间的关联规则,决定消息的路由方式。 - 路由键(Routing Key):
消息的路由规则,用于匹配绑定规则。 - 消息确认(Acknowledgment):
RabbitMQ 支持消息确认机制,确保消息从队列成功传递给消费者。
主要特点
- 跨语言支持:
RabbitMQ 支持多种编程语言(如 Python、Java、Go、C# 等),适合多样化的开发需求。 - 灵活的路由:
提供多种路由机制(如直连、主题、扇出、头部路由等),满足复杂的消息传递需求。 - 高可用性:
RabbitMQ 支持集群模式,确保系统的高可用性和可靠性。 - 插件支持:
提供丰富的插件,例如监控插件、Web 管理界面、分布式功能等。 - 消息持久化:
支持消息持久化到磁盘,避免消息因系统宕机而丢失。
常见使用场景
- 任务队列:
例如处理耗时任务时,将任务消息放入队列,由后台服务异步处理。 - 异步通信:
系统之间解耦,生产者发送消息后立即返回,不等待消费者处理完成。 - 消息广播:
通过扇出交换机将消息广播到多个队列,支持实时通知和多订阅者场景。 - 工作负载分发:
在多消费者之间分配任务,平衡负载。
以上为RabbitMQ的一些核心概念以及常见的使用场景。接下来,我们开始安装RibbitMQ并使用。
RibbitMQ安装
打开RibbitMQ官方文档 进入到以下页面
选择对应的操作系统下载,我这边使用的macOS的操作系统,所以直接使用brew安装即可
brew install rabbitmq
如果是其他系统的小伙伴,可选择对应的操作系统进行下载即可,这里就不展开说明了。 下载完成后,使用以下命令启动RibbitMQ:
brew services start rabbitmq
此时,我们访问http://localhost:15672/#/来到以下页面
账号密码默认都是
guest
,登录成功后,可以看到MQ的可视化面板如下图所示:
至此,RibbitMQ,安装已完成。
接下来,我们实现一个小demo,实现将订单通过api发送到RibbitMQ队列中,并实现从RibbitMQ队列消费订单数据,模拟订单支付处理的过程。
首先,我们安装依赖:
npm install amqplib express
而后编写send.js 消息发送方(生产者)
const express = require('express');
const amqp = require('amqplib/callback_api');
const app = express();
app.use(express.json());
let channel = null;
// 连接 RabbitMQ 并创建一个频道
amqp.connect('amqp://localhost:5672', (error, connection) => {
if (error) {
throw error;
}
connection.createChannel((err, ch) => {
if (err) {
throw err;
}
channel = ch;
// 创建队列
const queue = 'orderQueue';
channel.assertQueue(queue, { durable: true });
});
});
// 订单服务 API
app.post('/order', (req, res) => {
const order = { userId: req.body.userId, productId: req.body.productId };
// 将订单发送到 RabbitMQ 队列
channel.sendToQueue('orderQueue', Buffer.from(JSON.stringify(order)), {
persistent: true // 持久化消息(避免服务器宕机) 底层原理是 存储在磁盘
});
console.log("Order sent to queue:", order);
res.status(201).json({ message: 'Order placed successfully!' });
});
// 启动订单服务
app.listen(3008, () => {
console.log('Order service is running on port 3008');
});
启动send服务
node send.js
通过apifox去请求order接口:
此时,控制台将输出刚刚发送到RibbitMQ队列的信息
同时,我们可以通过可视化控制面板看到,我们刚刚创建的队列及待消费的数据数量监控
接下来,我们编写receive.js用于接收队列中的消息,并模拟处理的过程:
// payment-service.js 消费者
const amqp = require('amqplib/callback_api');
// 连接 RabbitMQ 并创建一个频道
amqp.connect('amqp://localhost:5672', (error, connection) => {
if (error) {
throw error;
}
connection.createChannel((err, channel) => {
if (err) {
throw err;
}
const queue = 'orderQueue';
/**
* durable: true:确保消息在服务器重启时不会丢失,将队列设置为持久性。
* 队列的结构和属性仍然会被保留,底层原理是,存储在磁盘。 缺点占用磁盘空间
* 优点:
* 持久性队列:适合需要确保队列结构不丢失的场景。
* 持久性消息:适合需要保证消息在系统崩溃时不丢失的场景
* 缺点:
* 每条消息都需要写入磁盘,可能导致延迟增加
* 持久性消息会占用更多的磁盘空间
* */
// 确保队列存在
channel.assertQueue(queue, {
durable: true // 队列和交换机的持久化
});
// 从队列中消费消息
console.log('Waiting for messages in %s. To exit press CTRL+C', queue);
channel.consume(queue, (msg) => {
const order = JSON.parse(msg.content.toString());
console.log('Received order:', order);
// 假设支付处理逻辑
setTimeout(() => {
console.log('Payment processed for order:', order);
channel.ack(msg); // 确认消息处理完成
}, 1000);
}, {
noAck: false // 确保消息处理完成后才确认
});
});
});
运行receive.js程序
node receive.js
此时,控制台将输出程序中消费队列中的数据的打印内容,如下:
(前面三个空对象是因为我在调用api时没有往body传值)
可以发现,刚刚我们send进队列中的五条订单数据,已经被消费了,同时也可以在可视化控制面板中看到:
以上,就是RibbitMQ的最简单工作原理。当然,这只是冰山一角,后续还有很多的知识,诸如交换机,死信队列,以及一些常见的参数,例如nack() 和 noAck()
的区别等等,这些都是需要继续进行深入学习的,一起加油吧!