环境:

MacOS 10.14

Node.js 8.9.1

零、背景


目前有个上线应用会接受多个请求,且每个请求的处理时间可能很久,可能到数小时,所以就想采用异步机制,至于复杂的运算就用消息队列(MQ)去慢慢消化。

网上调研了一圈,遂采用RabbitMQ。

一、安装


1、安装

(1) MacOS
brew install rabbitmq
(2) CentOS (Linux)

https://tecadmin.net/install-rabbitmq-on-centos/

2、配置环境变量

export PATH=$PATH:/usr/local/opt/rabbitmq/sbin

3、使用

(1) 服务器端

rabbitmq-server

启动需要(默认)200 MB的磁盘空间,但可以通过配置文件里的 disk_free_limit 修改。

(2) 客户端

以 Node.js 为例:

npm i amqplib

https://www.npmjs.com/package/amqplib

var amqp = require('amqplib/callback_api');

4、用 rabbitmq management 进行后台管理

(1) 开启服务

rabbitmq-plugins enable rabbitmq_management

此时/etc/rabbitmq下会多出enabled_plugins文件,内容为:

[rabbitmq_management].

此时 rabbitmq management 的地址为http://localhost:15672,默认用户密码为 guest/guest

但此时外网访问,登录时会提示 User can only log in via localhost

这是由于 rabbitmq 从 3.3.0 开始禁止使用 guest/guest 权限通过除 localhost 外的访问。

(2) 开启外网访问

1、rabbitmq 初始并没有创建配置文件,需要自行拷贝。

cp /usr/share/doc/rabbitmq-server-3.7.9/rabbitmq.config.example /etc/rabbitmq/rabbitmq.config

2、修改此配置文件rabbitmq.config

vim /etc/rabbitmq/rabbitmq.config

把 loopback_users 的注释解开:

%%{loopback_users, []} -> {loopback_users, []}

这里请小心 {loopback_users, []} 后的逗号可能需要去掉,不然格式会报错。

3、重启服务

sudo systemctl restart rabbitmq-server

注:可能在访问的时候会报这样的错:



解决办法: 关闭全局 ss 代理

二、使用


1、connection —— 连接

amqp.connect('amqp://localhost', function(error0, connection) {
if (error0) {
throw error0;
}
// ……
// connection.close();
});

2、channel —— 通道

通道分为:

生产者(发送者)

消费者(接收者)

connection.createChannel(function(error1, channel) {
if (error1) {
throw error1;
}
// channel ……
});
(1) queue —— 队列

队列里面塞入的是消息

生产者

var queue = 'queue_name';

# 创建or连上队列
channel.assertQueue(queue, {
durable: true # 队列持久化
});
# 临时队列(当前 connection 断掉后就会被删除)—— 队列名随机
channel.assertQueue('',{ exclusive:true }); # 将消息塞入队列
channel.sendToQueue(queue, Buffer.from(msg), {
persistent: true # 消息持久化
});
关于持久化:

一个是防止服务器端的队列丢失,一个是防止服务器端的队列里的消息丢失。

但是这并不能避免:如果服务器端在RabbitMQ接受消息的过程中挂了导致的消息丢失。如果需要更强的保证,可以使用 发布者确认

消费者

var queue = 'queue_name';

# 从队列取出消息
channel.consume(queue, function(msg) {
channel.ack(msg); # 发送确认信号
}, {
noAck: false
});
关于 ACK ( Acknowledgement )

noAck: true 则服务器端不会期望收到 ACK,也就是说,消息在被发送后会立即出列。

noAck: false 则需要消费者发送 ACK,即channel.ack(msg); ,但如果超时未回复 ACK,消息会重新排队(但如果同时有其他可用消费者,则会迅速安排过去)

查看当前有多少队列及各中有多少消息: sudo rabbitmqctl list_queues

(2) prefetch —— 预取
channel.prefetch(1);
# 表示这个通道如果有{1}个未完成的消息,则不会接受新的消息
(3) exchange —— 交换

如果有多个队列,生产者的消息应该如何分配呢?这个时候就需要一个中间件——交换

其中交换类型有四种:“”(默认交换), topic, headers, fanout

A、 “”(默认交换)

RabbitMQ中消息传递模型的核心思想是生产者永远不会将任何消息直接发送到队列。所以不建议使用channel.sendToQueue(),此为 “”(默认交换)。

如果没有队列绑定到交换,消息将会丢失。

B、fanout(广播)

生产者

var exchange = 'logs';

# 创建or连上交换
channel.assertExchange(exchange, 'fanout', {
durable: false # 持久化
}); ### 推消息给交换
channel.publish(exchange, '', Buffer.from(msg));

消费者

var exchange = 'logs';

# 创建or连上交换
channel.assertExchange(exchange, 'fanout', {
durable: false # 持久化
}); # ------------------------ # 绑定 交换+队列
channel.bindQueue('queue_1', exchange, '');
channel.bindQueue('queue_2', exchange, '');
channel.bindQueue('queue_3', exchange, '');

queue_1、queue_2、queue_3 都会收到相同的一条消息。

C、direct (直接)

生产者

var exchange = 'logs';

# 创建or连上交换
channel.assertExchange(exchange, 'fanout', {
durable: false # 持久化
}); ### 推消息给交换
channel.publish(exchange, 'black', Buffer.from(msg));

消费者

var exchange = 'logs';

# 创建or连上交换
channel.assertExchange(exchange, 'fanout', {
durable: false # 持久化
}); # ------------------------ # 绑定 交换+队列
channel.bindQueue('queue_1', exchange, 'white');
channel.bindQueue('queue_2', exchange, 'black');
channel.bindQueue('queue_3', exchange, 'red');

只有 queue_2 才会收到消息。

D、topic

生产者

var exchange = 'logs';

# 创建or连上交换
channel.assertExchange(exchange, 'fanout', {
durable: false # 持久化
}); ### 推消息给交换
channel.publish(exchange, 'kern.critical', Buffer.from(msg));

消费者

var exchange = 'logs';

# 创建or连上交换
channel.assertExchange(exchange, 'fanout', {
durable: false # 持久化
}); # ------------------------ # 绑定 交换+队列
channel.bindQueue('queue_1', exchange, '#');
channel.bindQueue('queue_2', exchange, "kern.*");
channel.bindQueue('queue_3', exchange, "*.critical");
  • *(星号)可以替代一个单词。
  • #(hash)可以替换零个或多个单词。

查看所有的 交换 及 交换绑定队列
sudo rabbitmqctl list_exchanges
sudo rabbitmqctl list_bindings
代码职责风格:

生产者只管发送消息就好 (比如发送消息给队列或者交换)

消费者要负责接受消息以外的更多事 (比如负责队列的 prefetch 设置,或者交换的绑定)

3、远程过程调用(RPC)

三、应用

例如可以用到日志系统中:对所有等级的日志都打印到控制台(即下面的队列),而 error 日志单独持久化到 disk(即上面的队列)。

四、MQ 的优缺点


优点:解耦、异步、削峰

缺点:系统可用性降低,系统复杂性增加


参考资料

1、官方RabbitMQ教程

https://www.rabbitmq.com/getstarted.html

2、amqp.node 参考API

https://www.squaremobius.net/amqp.node/channel_api.html#channel_ack

RabbitMQ 学习笔记的更多相关文章

  1. RabbitMQ学习笔记(五) Topic

    更多的问题 Direct Exchange帮助我们解决了分类发布与订阅消息的问题,但是Direct Exchange的问题是,它所使用的routingKey是一个简单字符串,这决定了它只能按照一个条件 ...

  2. RabbitMQ学习笔记1-hello world

    安装过程略过,一搜一大把. rabbitmq管理控制台:http://localhost:15672/   默认账户:guest/guest RabbitMQ默认监听端口:5672 JAVA API地 ...

  3. (转) Rabbitmq学习笔记

    详见原文: http://blog.csdn.net/shatty/article/details/9529463 Rabbitmq学习笔记

  4. 官网英文版学习——RabbitMQ学习笔记(十)RabbitMQ集群

    在第二节我们进行了RabbitMQ的安装,现在我们就RabbitMQ进行集群的搭建进行学习,参考官网地址是:http://www.rabbitmq.com/clustering.html 首先我们来看 ...

  5. 官网英文版学习——RabbitMQ学习笔记(一)认识RabbitMQ

    鉴于目前中文的RabbitMQ教程很缺,本博主虽然买了一本rabbitMQ的书,遗憾的是该书的代码用的不是java语言,看起来也有些不爽,且网友们不同人学习所写不同,本博主看的有些地方不太理想,为此本 ...

  6. RabbitMQ学习笔记五:RabbitMQ之优先级消息队列

    RabbitMQ优先级队列注意点: 1.只有当消费者不足,不能及时进行消费的情况下,优先级队列才会生效 2.RabbitMQ3.5以后才支持优先级队列 代码在博客:RabbitMQ学习笔记三:Java ...

  7. RabbitMQ学习笔记(六) RPC

    什么RPC? 这一段是从度娘摘抄的. RPC(Remote Procedure Call Protocol)——远程过程调用协议,它是一种通过网络从远程计算机程序上请求服务,而不需要了解底层网络技术的 ...

  8. 官网英文版学习——RabbitMQ学习笔记(八)Remote procedure call (RPC)

    在第四篇学习笔记中,我们学习了如何使用工作队列在多个工作者之间分配耗时的任务.   但是,如果我们需要在远程计算机上运行一个函数并等待结果呢?这是另一回事.这种模式通常称为远程过程调用或RPC.   ...

  9. 官网英文版学习——RabbitMQ学习笔记(二)RabbitMQ安装

    一.安装RabbitMQ的依赖Erlang 要进行RabbitMQ学习,首先需要进行RabbitMQ服务的安装,安装我们可以根据官网指导进行http://www.rabbitmq.com/downlo ...

  10. RabbitMQ学习笔记一

    前 言 -解决问题  一.RabbitMQ安装  1.安装erlang 环境 a.下载erlang 版本,注意这里需要和安装的rabbitMq版本相配对,rabbitMQ官方网站上可以查到:https ...

随机推荐

  1. Core在类中注入

    private readonly IHttpClientFactory _iHttpClientFactory; public static NetHelper Get = new NetHelper ...

  2. app:利用HBuilder打包webpack项目

    1.安装HBuilder 2.将你的项目在HBuilder中打开 3.控制台 打包编译 npm run build 4.新建一个app项目,将项目编译生成的dist文件夹 ,复制到app项目中 5.双 ...

  3. UOJ#206. 【APIO2016】Gap 构造 交互题

    原文链接www.cnblogs.com/zhouzhendong/p/UOJ206.html 题解 T = 1 的情况直接大力从两边向中间询问即可. T = 2 的情况挺妙的,我没想到. 考虑首先花费 ...

  4. Spring 1 控制反转、依赖注入

    1.1 Spring的核心是控制反转(IoC)和面向切面(AOP) 学习spring之前的开发中通过new创建一个对象,有了spring之后,spring创建对象实例-IoC控制反转,之后需要实例对象 ...

  5. SpringBoot主程序注解@SpringBootApplication简单分析

    一.@SpringBootApplication说明这个类是SpringBoot的主配置类,SpringBoot就应该运行这个类的main方法来启动SpringBoot应用: @SpringBootA ...

  6. 使用python来操作redis用法详解

    1.redis连接 redis提供两个类Redis和StrictRedis用于实现Redis的命令,StrictRedis用于实现大部分官方的命令,并使用官方的语法和命令,Redis是StrictRe ...

  7. jmeter主要函数助手功用说明

    jmeter中虽然有很多的插件,但是有些需要安装,有些具有一定的局限性.函数助手是一个快捷的工具库.下面记录一下函数助手中一些主要的函数的使用方法. 注:不内容中所有的实例均基于3.2记录 1._Be ...

  8. Little Sub and Mr.Potato's Math Problem-构造

    链接:http://acm.zju.edu.cn/onlinejudge/showProblem.do?problemId=5864 思路 : 判断小于它的合法的,再看大于它的合法的,特判10000. ...

  9. ios6和ios5横竖屏切换

    记录于2013/8/5   在切换横竖屏的时候调用到的一些委托方法: #pragma mark - UIApplicationDelegate //写在Appdelegate中,在具体的某一视图控制器 ...

  10. 我的 FPGA 学习历程(08)—— 实验:点亮单个数码管

    数码管是一种常见的用于显示的电子器件,根据数码管大致可以分为共阴极和共阳极两种,下图所示的是一个共阳极的数码管的电路图(摘自金沙滩工作室的 51 开发板电路图),我的 AX301 开发板与这张图的情况 ...