@azteam/rabbitmq-async 1.0.230 → 1.0.233
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/lib/Provider.js +46 -2
- package/lib/RabbitMQAsync.js +620 -252
- package/package.json +2 -2
- package/src/Provider.js +13 -2
- package/src/RabbitMQAsync.js +286 -101
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@azteam/rabbitmq-async",
|
|
3
|
-
"version": "1.0.
|
|
3
|
+
"version": "1.0.233",
|
|
4
4
|
"description": "N/A",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"toda",
|
|
@@ -16,7 +16,7 @@
|
|
|
16
16
|
"prepublishOnly": "babel src --delete-dir-on-start -d lib"
|
|
17
17
|
},
|
|
18
18
|
"dependencies": {
|
|
19
|
-
"@azteam/util": "1.0.
|
|
19
|
+
"@azteam/util": "1.0.65",
|
|
20
20
|
"amqplib": "0.8.0",
|
|
21
21
|
"fs-extra": "10.1.0",
|
|
22
22
|
"lodash": "4.17.23"
|
package/src/Provider.js
CHANGED
|
@@ -6,9 +6,8 @@ class Provider {
|
|
|
6
6
|
this.configs = {};
|
|
7
7
|
|
|
8
8
|
if (Array.isArray(configs)) {
|
|
9
|
-
configs.
|
|
9
|
+
configs.forEach((config) => {
|
|
10
10
|
this.configs[config.name] = config;
|
|
11
|
-
return true;
|
|
12
11
|
});
|
|
13
12
|
} else {
|
|
14
13
|
this.configs.main = configs;
|
|
@@ -34,6 +33,18 @@ class Provider {
|
|
|
34
33
|
})
|
|
35
34
|
);
|
|
36
35
|
}
|
|
36
|
+
|
|
37
|
+
async closeAll() {
|
|
38
|
+
await Promise.all(
|
|
39
|
+
Object.keys(this.connections).map(async (keyConfig) => {
|
|
40
|
+
const connection = this.connections[keyConfig];
|
|
41
|
+
if (connection) {
|
|
42
|
+
await connection.close();
|
|
43
|
+
}
|
|
44
|
+
})
|
|
45
|
+
);
|
|
46
|
+
this.connections = {};
|
|
47
|
+
}
|
|
37
48
|
}
|
|
38
49
|
|
|
39
50
|
export default Provider;
|
package/src/RabbitMQAsync.js
CHANGED
|
@@ -14,20 +14,17 @@ class RabbitMQAsync {
|
|
|
14
14
|
this.retryKey = config.retry_key;
|
|
15
15
|
|
|
16
16
|
this.listConsume = [];
|
|
17
|
+
this.listSub = [];
|
|
17
18
|
|
|
18
19
|
this.client = null;
|
|
19
20
|
this.channel = null;
|
|
21
|
+
this.reconnecting = false;
|
|
20
22
|
|
|
21
|
-
|
|
22
|
-
if (!this.connected) {
|
|
23
|
-
this.connect();
|
|
24
|
-
}
|
|
25
|
-
this.startListConsume();
|
|
26
|
-
}, 5000);
|
|
23
|
+
this.connect();
|
|
27
24
|
}
|
|
28
25
|
|
|
29
|
-
async waitConnection(n =
|
|
30
|
-
for (let i = 0; !this.connected
|
|
26
|
+
async waitConnection(n = 60) {
|
|
27
|
+
for (let i = 0; !this.connected && i < n; i += 1) {
|
|
31
28
|
await timeout(1000);
|
|
32
29
|
}
|
|
33
30
|
if (!this.connected) {
|
|
@@ -35,43 +32,82 @@ class RabbitMQAsync {
|
|
|
35
32
|
}
|
|
36
33
|
}
|
|
37
34
|
|
|
35
|
+
scheduleReconnect() {
|
|
36
|
+
if (this.reconnecting) return;
|
|
37
|
+
this.reconnecting = true;
|
|
38
|
+
setTimeout(() => {
|
|
39
|
+
this.reconnecting = false;
|
|
40
|
+
if (!this.connected) {
|
|
41
|
+
this.connect();
|
|
42
|
+
}
|
|
43
|
+
}, 5000).unref();
|
|
44
|
+
}
|
|
45
|
+
|
|
38
46
|
async connect() {
|
|
47
|
+
if (this.connected) return;
|
|
39
48
|
this._alert('connecting', 'MQ connecting...');
|
|
40
49
|
const opt = {credentials: amqp.credentials.plain(this.username, this.password)};
|
|
41
50
|
try {
|
|
42
|
-
this.client = await amqp.connect(`amqp://${this.host}
|
|
43
|
-
|
|
44
|
-
this.channel.prefetch(1);
|
|
45
|
-
this.connected = true;
|
|
46
|
-
this._alert('connect', 'MQ connected');
|
|
51
|
+
this.client = await amqp.connect(`amqp://${this.host}`, opt);
|
|
52
|
+
|
|
47
53
|
this.client.on('error', (err) => {
|
|
48
|
-
this.close();
|
|
49
54
|
this._alert('error', `MQ Error ${err}`);
|
|
55
|
+
this.handleDisconnect();
|
|
50
56
|
});
|
|
51
57
|
|
|
52
58
|
this.client.on('close', () => {
|
|
53
|
-
this.close();
|
|
54
59
|
this._alert('close', 'MQ closed');
|
|
60
|
+
this.handleDisconnect();
|
|
55
61
|
});
|
|
62
|
+
|
|
63
|
+
this.channel = await this.client.createChannel();
|
|
64
|
+
this.channel.prefetch(1);
|
|
65
|
+
this.connected = true;
|
|
66
|
+
this._alert('connect', 'MQ connected');
|
|
67
|
+
|
|
68
|
+
this.startListConsume();
|
|
69
|
+
this.startListSub();
|
|
56
70
|
} catch (err) {
|
|
57
|
-
this.
|
|
58
|
-
this.
|
|
71
|
+
this._alert('error', `MQ connect error: ${err}`);
|
|
72
|
+
this.handleDisconnect();
|
|
59
73
|
}
|
|
60
74
|
}
|
|
61
75
|
|
|
76
|
+
handleDisconnect() {
|
|
77
|
+
if (this.connected) {
|
|
78
|
+
this.connected = false;
|
|
79
|
+
this.listConsume.forEach((c) => {
|
|
80
|
+
c.listened = false;
|
|
81
|
+
});
|
|
82
|
+
this.listSub.forEach((s) => {
|
|
83
|
+
s.listened = false;
|
|
84
|
+
});
|
|
85
|
+
}
|
|
86
|
+
this.client = null;
|
|
87
|
+
this.channel = null;
|
|
88
|
+
this.scheduleReconnect();
|
|
89
|
+
}
|
|
90
|
+
|
|
62
91
|
async close() {
|
|
63
92
|
this.connected = false;
|
|
64
|
-
|
|
65
|
-
this.listConsume.map((c) => {
|
|
66
|
-
// eslint-disable-next-line no-param-reassign
|
|
93
|
+
this.listConsume.forEach((c) => {
|
|
67
94
|
c.listened = false;
|
|
68
95
|
});
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
}
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
96
|
+
this.listSub.forEach((s) => {
|
|
97
|
+
s.listened = false;
|
|
98
|
+
});
|
|
99
|
+
if (this.channel) {
|
|
100
|
+
try {
|
|
101
|
+
await this.channel.close();
|
|
102
|
+
} catch (err) {}
|
|
103
|
+
this.channel = null;
|
|
104
|
+
}
|
|
105
|
+
if (this.client) {
|
|
106
|
+
try {
|
|
107
|
+
await this.client.close();
|
|
108
|
+
} catch (err) {}
|
|
109
|
+
this.client = null;
|
|
110
|
+
}
|
|
75
111
|
}
|
|
76
112
|
|
|
77
113
|
parsePrefix(name) {
|
|
@@ -82,119 +118,150 @@ class RabbitMQAsync {
|
|
|
82
118
|
return newName;
|
|
83
119
|
}
|
|
84
120
|
|
|
85
|
-
async send(queueName, msg = {}, limit = 0) {
|
|
121
|
+
async send(queueName, msg = {}, limit = 0, retryCount = 0) {
|
|
122
|
+
if (retryCount >= 10) {
|
|
123
|
+
this._alert('error', `Failed to send to queue ${queueName} after 10 retries`);
|
|
124
|
+
return false;
|
|
125
|
+
}
|
|
86
126
|
try {
|
|
87
127
|
const prefixQueueName = this.parsePrefix(queueName);
|
|
88
|
-
if (this.connected) {
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
128
|
+
if (!this.connected || !this.channel) {
|
|
129
|
+
throw new Error('Not connected');
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
const queueInfo = await this.channel.assertQueue(prefixQueueName, {
|
|
133
|
+
durable: true,
|
|
134
|
+
});
|
|
135
|
+
|
|
136
|
+
if (limit === 0 || queueInfo.messageCount < limit) {
|
|
137
|
+
await this.channel.sendToQueue(prefixQueueName, Buffer.from(JSON.stringify({retry: 5, ...msg})), {
|
|
138
|
+
persistent: true,
|
|
139
|
+
});
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
return true;
|
|
143
|
+
} catch (err) {
|
|
144
|
+
await timeout(2000);
|
|
145
|
+
return this.send(queueName, msg, limit, retryCount + 1);
|
|
146
|
+
}
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
async pub(exchangeName, routingKey = '', msg = {}, type = 'fanout', retryCount = 0) {
|
|
150
|
+
if (retryCount >= 10) {
|
|
151
|
+
this._alert('error', `Failed to publish to exchange ${exchangeName} after 10 retries`);
|
|
152
|
+
return false;
|
|
153
|
+
}
|
|
154
|
+
try {
|
|
155
|
+
const prefixExchangeName = this.parsePrefix(exchangeName);
|
|
156
|
+
if (!this.connected || !this.channel) {
|
|
157
|
+
throw new Error('Not connected');
|
|
99
158
|
}
|
|
100
159
|
|
|
160
|
+
await this.channel.assertExchange(prefixExchangeName, type, {
|
|
161
|
+
durable: true,
|
|
162
|
+
});
|
|
163
|
+
|
|
164
|
+
await this.channel.publish(prefixExchangeName, routingKey, Buffer.from(JSON.stringify({retry: 5, ...msg})), {
|
|
165
|
+
persistent: true,
|
|
166
|
+
});
|
|
167
|
+
|
|
101
168
|
return true;
|
|
102
169
|
} catch (err) {
|
|
103
170
|
await timeout(2000);
|
|
104
|
-
return this.
|
|
171
|
+
return this.pub(exchangeName, routingKey, msg, type, retryCount + 1);
|
|
105
172
|
}
|
|
106
173
|
}
|
|
107
174
|
|
|
108
175
|
async getInfo(queueName) {
|
|
109
176
|
try {
|
|
110
177
|
const prefixQueueName = this.parsePrefix(queueName);
|
|
111
|
-
if (this.connected) {
|
|
112
|
-
|
|
113
|
-
return await channel.assertQueue(prefixQueueName, {
|
|
178
|
+
if (this.connected && this.channel) {
|
|
179
|
+
return await this.channel.assertQueue(prefixQueueName, {
|
|
114
180
|
durable: true,
|
|
115
181
|
});
|
|
116
182
|
}
|
|
117
183
|
} catch (err) {}
|
|
184
|
+
|
|
185
|
+
await timeout(2000);
|
|
118
186
|
return this.getInfo(queueName);
|
|
119
187
|
}
|
|
120
188
|
|
|
121
|
-
// eslint-disable-next-line consistent-return
|
|
122
189
|
async consume(queueName, asyncFunction, callbackError = null) {
|
|
123
190
|
const messageQueue = this;
|
|
124
191
|
try {
|
|
125
|
-
if (this.connected)
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
192
|
+
if (!this.connected || !this.channel) return;
|
|
193
|
+
|
|
194
|
+
const prefixQueueName = this.parsePrefix(queueName);
|
|
195
|
+
const {channel} = messageQueue;
|
|
196
|
+
|
|
197
|
+
await channel.assertQueue(prefixQueueName, {
|
|
198
|
+
durable: true,
|
|
199
|
+
});
|
|
200
|
+
|
|
201
|
+
await channel.consume(prefixQueueName, async function (msg) {
|
|
202
|
+
if (!msg) return;
|
|
203
|
+
|
|
204
|
+
try {
|
|
205
|
+
const data = JSON.parse(msg.content.toString());
|
|
206
|
+
if (data) {
|
|
207
|
+
try {
|
|
208
|
+
if (msg.fields.redelivered) {
|
|
209
|
+
data.retry = (data.retry || 0) - 1;
|
|
210
|
+
if (data.retry > 0) {
|
|
211
|
+
await messageQueue.send(queueName, data);
|
|
212
|
+
} else if (!data.notCheck && messageQueue.retryKey) {
|
|
213
|
+
await messageQueue.send(messageQueue.retryKey, {
|
|
214
|
+
queueName,
|
|
215
|
+
data,
|
|
216
|
+
retry_time: new Date(),
|
|
217
|
+
ip: messageQueue.serverIp,
|
|
218
|
+
});
|
|
219
|
+
}
|
|
220
|
+
await channel.ack(msg);
|
|
221
|
+
} else {
|
|
222
|
+
try {
|
|
223
|
+
await asyncFunction(data);
|
|
224
|
+
await channel.ack(msg);
|
|
225
|
+
} catch (err1) {
|
|
226
|
+
const errString = err1.toString();
|
|
227
|
+
if (_.some(['Channel closed', 'Channel closing'], (el) => _.includes(errString, el))) {
|
|
228
|
+
// Ignore
|
|
229
|
+
} else {
|
|
230
|
+
if (callbackError && data.retry === 1) {
|
|
231
|
+
callbackError(prefixQueueName, errString);
|
|
232
|
+
}
|
|
233
|
+
if (data.retry <= 0) {
|
|
150
234
|
await channel.ack(msg);
|
|
151
235
|
} else {
|
|
152
|
-
|
|
153
|
-
await asyncFunction(data);
|
|
154
|
-
await channel.ack(msg);
|
|
155
|
-
} catch (err1) {
|
|
156
|
-
const errString = err1.toString();
|
|
157
|
-
if (_.some(['Channel closed', 'Channel closing'], (el) => _.includes(errString, el))) {
|
|
158
|
-
reject(err1);
|
|
159
|
-
} else {
|
|
160
|
-
if (callbackError && data.retry === 1) {
|
|
161
|
-
callbackError(prefixQueueName, errString);
|
|
162
|
-
}
|
|
163
|
-
if (data.retry <= 0) {
|
|
164
|
-
await channel.ack(msg);
|
|
165
|
-
} else {
|
|
166
|
-
await channel.nack(msg);
|
|
167
|
-
}
|
|
168
|
-
}
|
|
169
|
-
}
|
|
236
|
+
await channel.nack(msg);
|
|
170
237
|
}
|
|
171
|
-
} catch (err2) {
|
|
172
|
-
reject(err2);
|
|
173
238
|
}
|
|
174
|
-
} else {
|
|
175
|
-
channel.ack(msg);
|
|
176
239
|
}
|
|
177
240
|
}
|
|
178
|
-
})
|
|
179
|
-
|
|
241
|
+
} catch (err2) {
|
|
242
|
+
messageQueue._alert('error', err2);
|
|
243
|
+
}
|
|
244
|
+
} else {
|
|
245
|
+
channel.ack(msg);
|
|
246
|
+
}
|
|
247
|
+
} catch (parseError) {
|
|
248
|
+
messageQueue._alert('error', parseError);
|
|
249
|
+
channel.ack(msg);
|
|
180
250
|
}
|
|
181
|
-
}
|
|
251
|
+
});
|
|
182
252
|
} catch (err3) {
|
|
183
|
-
|
|
184
|
-
this._alert('err3', err3);
|
|
253
|
+
this._alert('error', err3);
|
|
185
254
|
if (_.some(['Channel closed'], (el) => _.includes(err3.toString(), el))) {
|
|
186
|
-
messageQueue.
|
|
255
|
+
messageQueue.handleDisconnect();
|
|
187
256
|
}
|
|
188
257
|
}
|
|
189
258
|
}
|
|
190
259
|
|
|
191
260
|
startListConsume() {
|
|
192
|
-
if (this.connected) {
|
|
193
|
-
|
|
194
|
-
this.listConsume.map((c) => {
|
|
261
|
+
if (this.connected && this.channel) {
|
|
262
|
+
this.listConsume.forEach((c) => {
|
|
195
263
|
if (!c.listened) {
|
|
196
264
|
this.consume(c.queueName, c.asyncFunction, c.callbackError);
|
|
197
|
-
// eslint-disable-next-line no-param-reassign
|
|
198
265
|
c.listened = true;
|
|
199
266
|
}
|
|
200
267
|
});
|
|
@@ -209,6 +276,124 @@ class RabbitMQAsync {
|
|
|
209
276
|
callbackError,
|
|
210
277
|
listened: false,
|
|
211
278
|
});
|
|
279
|
+
if (this.connected) {
|
|
280
|
+
this.startListConsume();
|
|
281
|
+
}
|
|
282
|
+
}
|
|
283
|
+
}
|
|
284
|
+
|
|
285
|
+
async subscribe(exchangeName, routingKey, queueName, asyncFunction, callbackError = null, type = 'fanout') {
|
|
286
|
+
const messageQueue = this;
|
|
287
|
+
try {
|
|
288
|
+
if (!this.connected || !this.channel) return;
|
|
289
|
+
|
|
290
|
+
const prefixExchangeName = this.parsePrefix(exchangeName);
|
|
291
|
+
const prefixQueueName = queueName ? this.parsePrefix(queueName) : '';
|
|
292
|
+
const {channel} = messageQueue;
|
|
293
|
+
|
|
294
|
+
await channel.assertExchange(prefixExchangeName, type, {
|
|
295
|
+
durable: true,
|
|
296
|
+
});
|
|
297
|
+
|
|
298
|
+
const q = await channel.assertQueue(prefixQueueName, {
|
|
299
|
+
exclusive: !queueName,
|
|
300
|
+
durable: !!queueName,
|
|
301
|
+
});
|
|
302
|
+
|
|
303
|
+
await channel.bindQueue(q.queue, prefixExchangeName, routingKey);
|
|
304
|
+
|
|
305
|
+
await channel.consume(q.queue, async function (msg) {
|
|
306
|
+
if (!msg) return;
|
|
307
|
+
|
|
308
|
+
try {
|
|
309
|
+
const data = JSON.parse(msg.content.toString());
|
|
310
|
+
if (data) {
|
|
311
|
+
try {
|
|
312
|
+
if (msg.fields.redelivered && queueName) {
|
|
313
|
+
// named queue error retry flow
|
|
314
|
+
data.retry = (data.retry || 0) - 1;
|
|
315
|
+
if (data.retry > 0) {
|
|
316
|
+
await messageQueue.send(queueName, data);
|
|
317
|
+
} else if (!data.notCheck && messageQueue.retryKey) {
|
|
318
|
+
await messageQueue.send(messageQueue.retryKey, {
|
|
319
|
+
queueName: q.queue,
|
|
320
|
+
data,
|
|
321
|
+
retry_time: new Date(),
|
|
322
|
+
ip: messageQueue.serverIp,
|
|
323
|
+
});
|
|
324
|
+
}
|
|
325
|
+
await channel.ack(msg);
|
|
326
|
+
} else {
|
|
327
|
+
try {
|
|
328
|
+
await asyncFunction(data);
|
|
329
|
+
await channel.ack(msg);
|
|
330
|
+
} catch (err1) {
|
|
331
|
+
const errString = err1.toString();
|
|
332
|
+
if (_.some(['Channel closed', 'Channel closing'], (el) => _.includes(errString, el))) {
|
|
333
|
+
// Ignore
|
|
334
|
+
} else {
|
|
335
|
+
if (callbackError && data.retry === 1) {
|
|
336
|
+
callbackError(q.queue, errString);
|
|
337
|
+
}
|
|
338
|
+
if (queueName) {
|
|
339
|
+
if (data.retry <= 0) {
|
|
340
|
+
await channel.ack(msg);
|
|
341
|
+
} else {
|
|
342
|
+
await channel.nack(msg);
|
|
343
|
+
}
|
|
344
|
+
} else {
|
|
345
|
+
await channel.nack(msg, false, false);
|
|
346
|
+
}
|
|
347
|
+
}
|
|
348
|
+
}
|
|
349
|
+
}
|
|
350
|
+
} catch (err2) {
|
|
351
|
+
messageQueue._alert('error', err2);
|
|
352
|
+
}
|
|
353
|
+
} else {
|
|
354
|
+
channel.ack(msg);
|
|
355
|
+
}
|
|
356
|
+
} catch (parseError) {
|
|
357
|
+
messageQueue._alert('error', parseError);
|
|
358
|
+
channel.ack(msg);
|
|
359
|
+
}
|
|
360
|
+
});
|
|
361
|
+
} catch (err3) {
|
|
362
|
+
this._alert('error', err3);
|
|
363
|
+
if (_.some(['Channel closed'], (el) => _.includes(err3.toString(), el))) {
|
|
364
|
+
messageQueue.handleDisconnect();
|
|
365
|
+
}
|
|
366
|
+
}
|
|
367
|
+
}
|
|
368
|
+
|
|
369
|
+
startListSub() {
|
|
370
|
+
if (this.connected && this.channel) {
|
|
371
|
+
this.listSub.forEach((s) => {
|
|
372
|
+
if (!s.listened) {
|
|
373
|
+
this.subscribe(s.exchangeName, s.routingKey, s.queueName, s.asyncFunction, s.callbackError, s.type);
|
|
374
|
+
s.listened = true;
|
|
375
|
+
}
|
|
376
|
+
});
|
|
377
|
+
}
|
|
378
|
+
}
|
|
379
|
+
|
|
380
|
+
async sub(exchangeName, routingKey, queueName, asyncFunction, callbackError = null, type = 'fanout') {
|
|
381
|
+
const existing = queueName
|
|
382
|
+
? _.find(this.listSub, (s) => s.exchangeName === exchangeName && s.routingKey === routingKey && s.queueName === queueName)
|
|
383
|
+
: false;
|
|
384
|
+
if (!existing) {
|
|
385
|
+
this.listSub.push({
|
|
386
|
+
exchangeName,
|
|
387
|
+
routingKey,
|
|
388
|
+
queueName,
|
|
389
|
+
asyncFunction,
|
|
390
|
+
callbackError,
|
|
391
|
+
type,
|
|
392
|
+
listened: false,
|
|
393
|
+
});
|
|
394
|
+
if (this.connected) {
|
|
395
|
+
this.startListSub();
|
|
396
|
+
}
|
|
212
397
|
}
|
|
213
398
|
}
|
|
214
399
|
|
|
@@ -220,7 +405,7 @@ class RabbitMQAsync {
|
|
|
220
405
|
if (typeof this.alertCallback === 'function') {
|
|
221
406
|
this.alertCallback(status, msg);
|
|
222
407
|
} else {
|
|
223
|
-
console.error(status
|
|
408
|
+
console.error(`[RabbitMQAsync][${status}]`, msg);
|
|
224
409
|
}
|
|
225
410
|
}
|
|
226
411
|
}
|