This repository was archived by the owner on Nov 15, 2024. It is now read-only.
-
Notifications
You must be signed in to change notification settings - Fork 36
Expand file tree
/
Copy pathrabbitmq.js
More file actions
131 lines (116 loc) · 3.04 KB
/
Copy pathrabbitmq.js
File metadata and controls
131 lines (116 loc) · 3.04 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
// This file provides a simple message queueing service using RabbitMQ (AMQP protocol)
//
// NOTE: requires the delayed message exchange plugin to be enabled in the rabbitmq server:
// https://github.com/rabbitmq/rabbitmq-delayed-message-exchange
'use strict';
const AMQP = require('amqplib');
class RabbitMQClient {
constructor (options = {}) {
Object.assign(this, options);
this.queues = {};
}
// initialize
async init () {
this.rabbitmq = await AMQP.connect(this.host);
}
// create a queue given the name provided, messages will be returned in the handler callback provided
async createQueue (options) {
const { name } = options;
if (!name) {
throw 'must provide a queue name';
}
const channel = await this.rabbitmq.createChannel();
await channel.assertExchange(
name,
'x-delayed-message',
{
durable: true,
arguments: {
'x-delayed-type': 'fanout'
}
}
);
await channel.assertQueue(name, { durable: true });
await channel.bindQueue(name, name, '');
channel.on('error', error => {
this.warn(`RabbitMQ handler error: ${error.message}`);
});
this.queues[options.name] = {
name: options.name,
channel
};
}
// start listening to the specified queue
async listen (options) {
const { name } = options;
const queue = this.queues[name];
if (!queue) {
throw `cannot listen to queue ${options.name}, queue has not been created yet`;
}
queue.handler = options.handler;
const result = await queue.channel.consume(name, this._processMessage.bind(this));
queue.tag = result.consumerTag;
}
// stop listening on the given queue
stopListening (queueName) {
const queue = this.queues[queueName];
if (!queue || !queue.channel || !queue.tag) {
return;
}
queue.channel.cancel(queue.tag);
}
// send a message to the given message queue
async sendMessage (queueName, data, options) {
options = options || {};
if (typeof queueName !== 'string') {
throw 'must provide a valid queue name';
}
const queue = this.queues[queueName];
if (!queue) {
throw `no queue found matching ${queueName}`;
}
const publishOptions = {
persistent: true
};
if (options.delay) {
publishOptions.headers = {
'x-delay': options.delay * 1000
};
}
queue.channel.publish(
queueName,
'',
Buffer.from(JSON.stringify(data)),
publishOptions
);
}
// process data for a single message data from the given queue
_processMessage (message) {
const { content, fields } = message;
const name = fields.exchange;
this.log(`Received a RabbitMQ message on queue ${name}`);
const queue = this.queues[name];
if (!queue) { return; }
queue.channel.ack(message);
if (!content || !queue.handler) { return; }
let data;
try {
data = JSON.parse(content);
}
catch (error) {
this.warn(`Unable to process message on queue ${name}: bad JSON data: ${error}`);
}
queue.handler(data);
}
log (message) {
if (this.logger) {
this.logger.log(message);
}
}
warn (message) {
if (this.logger) {
this.logger.warn(message);
}
}
}
module.exports = RabbitMQClient;