由于项目需要使用消息队列来处理异步服务,所以需要安装消息队列。对比RabbitMQ、ActiveMQ、RocketMQ之后,发现RabbitMQ支持nodejs,同时也是开源社区较为活跃的消息中间件,所以决定选择RabbitMQ。相关安装步骤如下:
执行cat /etc/issue
即可获取linux版本,以便下载对应版本的RabbitMQ。目前服务器版本是Centos6.8,所以后续都是基于此版本进行,其他版本可参照。
Erlang是RabbitMQ的依赖,所以在安装RabbitMQ之前,需要先安装Erlang。
wget https://packages.erlang-solutions.com/erlang-solutions-1.0-1.noarch.rpm
rpm -Uvh erlang-solutions-1.0-1.noarch.rpm
rpm –import https://packages.erlang-solutions.com/rpm/erlang_solutions.asc
修改/etc/yum.repos.d/erlang_solutions.repo
如下:
[erlang-solutions]
name=Centos releasever− basearch - Erlang Solutions
baseurl=https://packages.erlang-solutions.com/rpm/centos/ releasever/ basearch
gpgcheck=1
gpgkey=https://packages.erlang-solutions.com/rpm/erlang_solutions.asc
enabled=1
之后,安装Erlang。
sudo yum install erlang
or sudo yum install esl-erlang
存在提示“apt.sw.be”错误时,可以通过如下方式解决:
rpm -e rpmforge-release –nodeps
yum clean all
yum update
安装Erlang之后,即可安装RabbitMQ。
rpm –import https://www.rabbitmq.com/rabbitmq-release-signing-key.asc
yum install rabbitmq-server-3.6.9-1.el6.noarch.rpm
或者直接rpm包安装
wget http://www.rabbitmq.com/releases/rabbitmq-server/v3.6.10/rabbitmq-server-3.6.9-1.el6.noarch.rpm
rpm -Uvh rabbitmq-server-3.6.9-1.el6.noarch.rpm
首先,启动RabbitMQ:
rabbitmq-server -detached
然后,配置网页插件,实现通过网页查看RabbitMQ的状态信息。
sudo rabbitmq-plugins enable rabbitmq_management
配置完成后,可以通过http://ip:15672
(默认端口是15672,登录用户名、密码需要重新设置,guest用户只能localhost访问)。
添加用户以及设置用户权限的操作如下:
sudo rabbitmqctl add_user root 123456
sudo rabbitmqctl set_permissions -p “/” root “.” “.” “.*”
sudo rabbitmqctl set_user_tags root administrator
这样,通过用户root即可远程访问RabbitMQ。
目前,项目使用的egg作为后台开发框架,所以RabbitMQ的client程序选择amqp.node,与RabbitMQ教程中使用的包一致。
由于egg支持生成器generate模式,而amqp.node默认的Promise模式中sendToQueue函数仍然是异步的,而且无法使用yield,所以需要ES6中的Promise进行一次封装。处理方式是在运用egg的extend模式,在context下增加一个sendTasksToQueue和consumeTasksFromQueue函数来处理消息任务,及在app/extend/context.js
中添加代码如下:
'use strict';
const amqp = require('amqplib/callback_api');
module.exports = {
sendTasksToQueue(queue, msg) {
return new Promise((resolve, reject) => {
amqp.connect('amqp://user:password@ip:5672', (err, conn) => {
if (err) {
reject({
status: false,
message: '无法建立连接'
});
return;
}
conn.createChannel((err, ch) => {
if (err) {
reject({
status: false,
message: '无法建立channel'
});
return;
}
ch.assertQueue(queue, { durable: true });
ch.sendToQueue(queue, new Buffer(msg));
resolve();
});
setTimeout(() => {
conn.close();
}, 500);
});
})
},
consumeTasksFromQueue(queue) {
return new Promise((resolve, reject) => {
amqp.connect('amqp://user:password@ip:5672', (err, conn) => {
if (err) {
reject({
status: false,
message: '无法建立连接'
});
return;
}
conn.createChannel((err, ch) => {
if (err) {
reject({
status: false,
message: '无法建立channel'
});
return;
}
ch.assertQueue(queue, { durable: true });
ch.consume(queue, (msg) => {
resolve({
status: true,
data: msg.content.toString()
});
}, {
noAck: true
});
});
setTimeout(() => {
conn.close();
}, 500);
});
})
}
}
然后,在对应的controller
或者schedule
中调用这两个函数即可实现消息的publish及consume。