@autofleet/rabbit 3.2.21-beta.1 → 3.2.22-beta.0
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/dist/index.d.ts +27 -14
- package/dist/index.js +62 -50
- package/dist/lib/types.d.ts +9 -7
- package/dist/lib/types.js +6 -1
- package/package.json +2 -4
- package/src/index.ts +96 -70
- package/src/lib/types.ts +11 -9
package/dist/index.d.ts
CHANGED
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
/// <reference types="node" />
|
|
2
2
|
import { EventEmitter } from 'events';
|
|
3
|
-
import { AmqpConnectionManager, ChannelWrapper } from 'amqp-connection-manager';
|
|
3
|
+
import { AmqpConnectionManager, ChannelWrapper, CreateChannelOpts } from 'amqp-connection-manager';
|
|
4
4
|
import { ConfirmChannel, ConsumeMessage, Options, Replies } from 'amqplib';
|
|
5
5
|
import { RedisConfig } from './lib/redis';
|
|
6
|
-
import { CallbackFunction, ConsumeMessageOrNull, ConsumeOptions, CustomMessageHeaders, QueuesCache, RedisLockType, ExchangesCache, QueueSetupPromisesDictionary,
|
|
6
|
+
import { CallbackFunction, ConsumeMessageOrNull, ConsumeOptions, CustomMessageHeaders, QueuesCache, RedisLockType, ExchangesCache, QueueSetupPromisesDictionary, ConnectionPurpose, ConnectionData } from './lib/types';
|
|
7
7
|
export interface IAfRabbitMq {
|
|
8
8
|
ack: any;
|
|
9
9
|
nack: any;
|
|
@@ -33,6 +33,17 @@ export interface AfRabbitOptions {
|
|
|
33
33
|
dontRetryAssert?: boolean;
|
|
34
34
|
rabbitHost?: string;
|
|
35
35
|
}
|
|
36
|
+
type newChannelOpts = {
|
|
37
|
+
name?: string;
|
|
38
|
+
onClose?: null | ((args: any | null) => void);
|
|
39
|
+
options?: CreateChannelOpts | undefined;
|
|
40
|
+
connectionPurpose: ConnectionPurpose;
|
|
41
|
+
};
|
|
42
|
+
type assertChannelOpts = {
|
|
43
|
+
channelName?: string;
|
|
44
|
+
force?: boolean;
|
|
45
|
+
connectionPurpose: ConnectionPurpose;
|
|
46
|
+
};
|
|
36
47
|
declare class RabbitMq implements IAfRabbitMq {
|
|
37
48
|
static parseMsg(msg: any): any;
|
|
38
49
|
static validateName(type: string, name: string): void;
|
|
@@ -51,9 +62,11 @@ declare class RabbitMq implements IAfRabbitMq {
|
|
|
51
62
|
channel: ChannelWrapper | null;
|
|
52
63
|
publishChannelSetupPromise: Promise<ChannelWrapper> | null;
|
|
53
64
|
blockReconnect: boolean | null | undefined;
|
|
54
|
-
|
|
65
|
+
connectionsMap: {
|
|
66
|
+
[ConnectionPurpose.Consume]: ConnectionData;
|
|
67
|
+
[ConnectionPurpose.Publish]: ConnectionData;
|
|
68
|
+
};
|
|
55
69
|
em: EventEmitter;
|
|
56
|
-
creatingConnection: boolean;
|
|
57
70
|
exchanges: ExchangesCache;
|
|
58
71
|
queues: QueuesCache;
|
|
59
72
|
queueSetupPromises: QueueSetupPromisesDictionary;
|
|
@@ -67,24 +80,24 @@ declare class RabbitMq implements IAfRabbitMq {
|
|
|
67
80
|
private shouldConsumeMessageByTimestamp;
|
|
68
81
|
ack: (channel: ConfirmChannel, msg: ConsumeMessageOrNull, shouldUpdateRedisTimestamp?: boolean, releaseLock?: null) => (userMsg: ConsumeMessage) => Promise<any>;
|
|
69
82
|
nack: (channel: ConfirmChannel, queue: string, options: any, deadQueueOptions: Options.AssertQueue, msg: ConsumeMessageOrNull, releaseLock: any) => (userMsg: ConsumeMessageOrNull, { skipRetry, }?: NackOptions) => Promise<any>;
|
|
70
|
-
getConnection(): Promise<AmqpConnectionManager>;
|
|
71
|
-
getNewChannel({ name, onClose, options }
|
|
72
|
-
assertChannel({ force,
|
|
73
|
-
assertExchange(exchangeName: string): Promise<any>;
|
|
74
|
-
getQueueLength(queue: string): Promise<Replies.AssertQueue>;
|
|
83
|
+
getConnection(connectionPurpose: ConnectionPurpose): Promise<AmqpConnectionManager | null | undefined>;
|
|
84
|
+
getNewChannel({ name, onClose, options, connectionPurpose, }: newChannelOpts): Promise<ChannelWrapper>;
|
|
85
|
+
assertChannel({ force, connectionPurpose }: assertChannelOpts): Promise<ChannelWrapper>;
|
|
86
|
+
assertExchange(exchangeName: string, options?: any): Promise<any>;
|
|
87
|
+
getQueueLength(queue: string, connectionPurpose?: ConnectionPurpose): Promise<Replies.AssertQueue>;
|
|
75
88
|
private deleteQueue;
|
|
76
|
-
bindQueue(queue: string, exchange: string): Promise<void>;
|
|
77
|
-
setupQueue(queueName: string, options?: Options.AssertQueue): Promise<Replies.AssertQueue>;
|
|
78
|
-
assertQueue(queueName: string, options?: Options.AssertQueue): Promise<any>;
|
|
89
|
+
bindQueue(queue: string, exchange: string, connectionPurpose: ConnectionPurpose): Promise<void>;
|
|
90
|
+
setupQueue(queueName: string, connectionPurpose: ConnectionPurpose, options?: Options.AssertQueue): Promise<Replies.AssertQueue>;
|
|
91
|
+
assertQueue(queueName: string, connectionPurpose: ConnectionPurpose, options?: Options.AssertQueue): Promise<any>;
|
|
79
92
|
private saveConsumer;
|
|
80
93
|
consume(queue: string, callback: CallbackFunction, options?: ConsumeOptions): Promise<any>;
|
|
81
94
|
private lockRedisIfNeeded;
|
|
82
95
|
private unlockRedisIfNeeded;
|
|
83
96
|
private consumeFromRabbit;
|
|
84
97
|
consumeFromExchange(queue: string, exchange: string, callback: CallbackFunction, options?: ConsumeOptions): Promise<any>;
|
|
85
|
-
publish(exchange: string, content: any, customHeaders?: any
|
|
98
|
+
publish(exchange: string, content: any, customHeaders?: any): Promise<boolean>;
|
|
86
99
|
sendToQueue(queue: string, content: any, options?: any, customHeaders?: any): Promise<boolean | undefined>;
|
|
87
|
-
isConnected(): Promise<boolean>;
|
|
100
|
+
isConnected(connectionPurpose: ConnectionPurpose): Promise<boolean>;
|
|
88
101
|
gracefulShutdown(signal: string): Promise<void>;
|
|
89
102
|
}
|
|
90
103
|
export default RabbitMq;
|
package/dist/index.js
CHANGED
|
@@ -6,7 +6,6 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
|
6
6
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
7
7
|
const events_1 = require("events");
|
|
8
8
|
const util_1 = require("util");
|
|
9
|
-
const lodash_1 = require("lodash");
|
|
10
9
|
const moment_1 = __importDefault(require("moment"));
|
|
11
10
|
const redis_lock_1 = __importDefault(require("redis-lock"));
|
|
12
11
|
const amqp_connection_manager_1 = require("amqp-connection-manager");
|
|
@@ -119,8 +118,10 @@ class RabbitMq {
|
|
|
119
118
|
this.em = new events_1.EventEmitter();
|
|
120
119
|
this.channel = null;
|
|
121
120
|
this.publishChannelSetupPromise = null;
|
|
122
|
-
this.
|
|
123
|
-
|
|
121
|
+
this.connectionsMap = {
|
|
122
|
+
[types_1.ConnectionPurpose.Consume]: { connection: null, creatingConnection: false },
|
|
123
|
+
[types_1.ConnectionPurpose.Publish]: { connection: null, creatingConnection: false },
|
|
124
|
+
};
|
|
124
125
|
this.exchanges = {};
|
|
125
126
|
this.queues = {};
|
|
126
127
|
this.queueSetupPromises = {};
|
|
@@ -141,30 +142,31 @@ class RabbitMq {
|
|
|
141
142
|
});
|
|
142
143
|
}
|
|
143
144
|
}
|
|
144
|
-
async getConnection() {
|
|
145
|
+
async getConnection(connectionPurpose) {
|
|
145
146
|
return new Promise(async (resolve, reject) => {
|
|
147
|
+
const { connection, creatingConnection } = this.connectionsMap[connectionPurpose];
|
|
146
148
|
if (this.blockReconnect) {
|
|
147
149
|
debug('rabbit: block reconnect');
|
|
148
150
|
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
|
|
149
151
|
// @ts-ignore
|
|
150
152
|
return resolve();
|
|
151
153
|
}
|
|
152
|
-
if (
|
|
153
|
-
if (this.options?.disableReconnect ||
|
|
154
|
+
if (connection !== null) {
|
|
155
|
+
if (this.options?.disableReconnect || connection?.isConnected()) {
|
|
154
156
|
debug('rabbit: connection - is connected');
|
|
155
157
|
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
|
|
156
158
|
// @ts-ignore
|
|
157
|
-
return resolve(
|
|
159
|
+
return resolve(connection);
|
|
158
160
|
}
|
|
159
161
|
debug('rabbit: connection - reconnecting');
|
|
160
162
|
}
|
|
161
|
-
if (
|
|
163
|
+
if (creatingConnection) {
|
|
162
164
|
debug('rabbit: creating connection emi');
|
|
163
165
|
this.em.once(consts_1.CONNECTION_CREATED_CONST, resolve);
|
|
164
166
|
this.em.once(consts_1.CONNECTION_FAILED_CONST, reject);
|
|
165
167
|
return;
|
|
166
168
|
}
|
|
167
|
-
this.creatingConnection = true;
|
|
169
|
+
this.connectionsMap[connectionPurpose].creatingConnection = true;
|
|
168
170
|
let isResolved = false;
|
|
169
171
|
// It is import to use it as a function and not as a variable
|
|
170
172
|
// because of k8s changes the env variables
|
|
@@ -177,11 +179,15 @@ class RabbitMq {
|
|
|
177
179
|
return [`amqp://${userName}:${password}@${host}?heartbeat=${HEARTBEAT}`];
|
|
178
180
|
};
|
|
179
181
|
const defaultUrls = findServers();
|
|
180
|
-
const
|
|
182
|
+
const newConnection = await (0, amqp_connection_manager_1.connect)(defaultUrls, {
|
|
181
183
|
findServers,
|
|
182
184
|
});
|
|
183
|
-
|
|
184
|
-
|
|
185
|
+
if (!newConnection) {
|
|
186
|
+
logger_1.default.error('rabbit: couldnt create a connection');
|
|
187
|
+
return resolve(connection);
|
|
188
|
+
}
|
|
189
|
+
this.connectionsMap[connectionPurpose].connection = newConnection;
|
|
190
|
+
newConnection.on('error', (err) => {
|
|
185
191
|
logger_1.default.error('rabbit: connection error', { err });
|
|
186
192
|
if (!isResolved) {
|
|
187
193
|
isResolved = true;
|
|
@@ -189,7 +195,7 @@ class RabbitMq {
|
|
|
189
195
|
this.em.emit(consts_1.CONNECTION_FAILED_CONST, err);
|
|
190
196
|
}
|
|
191
197
|
});
|
|
192
|
-
|
|
198
|
+
newConnection.on('connectFailed', (err) => {
|
|
193
199
|
this.consumersTags = [];
|
|
194
200
|
logger_1.default.error('rabbit: connection connectFailed', { err });
|
|
195
201
|
if (!isResolved) {
|
|
@@ -198,8 +204,7 @@ class RabbitMq {
|
|
|
198
204
|
this.em.emit(consts_1.CONNECTION_FAILED_CONST, err);
|
|
199
205
|
}
|
|
200
206
|
});
|
|
201
|
-
|
|
202
|
-
// this.channel = null;
|
|
207
|
+
newConnection.on('disconnect', ({ err }) => {
|
|
203
208
|
this.consumersTags = [];
|
|
204
209
|
debug('rabbit: connection closed');
|
|
205
210
|
if (this.options?.disableReconnect) {
|
|
@@ -210,24 +215,27 @@ class RabbitMq {
|
|
|
210
215
|
logger_1.default.error(`${this.RECONNECT_MSG}${err && ` - ${err}`}`);
|
|
211
216
|
}
|
|
212
217
|
});
|
|
213
|
-
|
|
218
|
+
newConnection.once('connect', async () => {
|
|
214
219
|
debug('rabbit: connection established');
|
|
215
|
-
this.creatingConnection = false;
|
|
216
|
-
this.em.emit(consts_1.CONNECTION_CREATED_CONST,
|
|
220
|
+
this.connectionsMap[connectionPurpose].creatingConnection = false;
|
|
221
|
+
this.em.emit(consts_1.CONNECTION_CREATED_CONST, newConnection);
|
|
217
222
|
isResolved = true;
|
|
218
|
-
resolve(
|
|
223
|
+
resolve(newConnection);
|
|
219
224
|
});
|
|
220
225
|
});
|
|
221
226
|
}
|
|
222
|
-
async getNewChannel({ name = (0, utils_1.rand)().toString(), onClose = null, options = {}
|
|
227
|
+
async getNewChannel({ name = (0, utils_1.rand)().toString(), onClose = null, options = {}, connectionPurpose = types_1.ConnectionPurpose.Consume, }) {
|
|
223
228
|
let connection;
|
|
224
229
|
try {
|
|
225
|
-
connection = await this.getConnection();
|
|
230
|
+
connection = await this.getConnection(connectionPurpose);
|
|
226
231
|
}
|
|
227
232
|
catch (e) {
|
|
228
233
|
logger_1.default.error(`rabbit: error on get connection for new channel ${name} `, { e });
|
|
229
234
|
throw e;
|
|
230
235
|
}
|
|
236
|
+
if (!connection) {
|
|
237
|
+
throw new Error(`rabbit: couldnt get connection for new channel ${name}`);
|
|
238
|
+
}
|
|
231
239
|
const channel = connection.createChannel({ ...options });
|
|
232
240
|
(0, events_1.once)(channel, 'close').then((args) => {
|
|
233
241
|
logger_1.default.error(`rabbit: channel ${name} closed`);
|
|
@@ -243,14 +251,14 @@ class RabbitMq {
|
|
|
243
251
|
throw err;
|
|
244
252
|
}
|
|
245
253
|
}
|
|
246
|
-
async assertChannel({ force = false,
|
|
254
|
+
async assertChannel({ force = false, connectionPurpose = types_1.ConnectionPurpose.Consume }) {
|
|
247
255
|
if (!this.publishChannelSetupPromise) {
|
|
248
256
|
this.publishChannelSetupPromise = new Promise(async (resolve, reject) => {
|
|
249
257
|
if (this.channel && !force) {
|
|
250
258
|
return resolve(this.channel);
|
|
251
259
|
}
|
|
252
260
|
try {
|
|
253
|
-
const channel = await this.getNewChannel({
|
|
261
|
+
const channel = await this.getNewChannel({ connectionPurpose });
|
|
254
262
|
channel.on('error', (err) => {
|
|
255
263
|
logger_1.default.error('rabbit: channel error', { err });
|
|
256
264
|
});
|
|
@@ -264,8 +272,8 @@ class RabbitMq {
|
|
|
264
272
|
}
|
|
265
273
|
return this.publishChannelSetupPromise;
|
|
266
274
|
}
|
|
267
|
-
async assertExchange(exchangeName) {
|
|
268
|
-
const channel = await this.assertChannel();
|
|
275
|
+
async assertExchange(exchangeName, options) {
|
|
276
|
+
const channel = await this.assertChannel({ connectionPurpose: options.connectionPurpose });
|
|
269
277
|
if (this.exchanges[exchangeName]) {
|
|
270
278
|
return this.exchanges[exchangeName];
|
|
271
279
|
}
|
|
@@ -273,32 +281,33 @@ class RabbitMq {
|
|
|
273
281
|
this.exchanges[exchangeName] = exchange;
|
|
274
282
|
return exchange;
|
|
275
283
|
}
|
|
276
|
-
async getQueueLength(queue) {
|
|
284
|
+
async getQueueLength(queue, connectionPurpose = types_1.ConnectionPurpose.Consume) {
|
|
277
285
|
RabbitMq.validateName('queue', queue);
|
|
286
|
+
const { connection } = this.connectionsMap[connectionPurpose];
|
|
278
287
|
const { channel } = this;
|
|
279
288
|
if (!channel) {
|
|
280
289
|
throw new Error('channel is not defined');
|
|
281
290
|
}
|
|
282
|
-
debug('rabbit: getting queue length', { queue, connected:
|
|
291
|
+
debug('rabbit: getting queue length', { queue, connected: connection?.isConnected() });
|
|
283
292
|
return channel?.checkQueue(queue);
|
|
284
293
|
}
|
|
285
|
-
async deleteQueue(queue) {
|
|
294
|
+
async deleteQueue(queue, connectionPurpose) {
|
|
286
295
|
RabbitMq.validateName('queue', queue);
|
|
287
|
-
const channel = await this.assertChannel();
|
|
296
|
+
const channel = await this.assertChannel({ connectionPurpose });
|
|
288
297
|
logger_1.default.info('rabbit: deleting queue', { queue });
|
|
289
298
|
const deleteQueueRes = await channel.deleteQueue(queue);
|
|
290
299
|
debug('queue deleted', deleteQueueRes);
|
|
291
300
|
return deleteQueueRes;
|
|
292
301
|
}
|
|
293
|
-
async bindQueue(queue, exchange) {
|
|
294
|
-
const channel = await this.assertChannel();
|
|
302
|
+
async bindQueue(queue, exchange, connectionPurpose) {
|
|
303
|
+
const channel = await this.assertChannel({ connectionPurpose });
|
|
295
304
|
await channel.addSetup((setupChannel) => setupChannel.bindQueue(queue, exchange, ''));
|
|
296
305
|
return channel.bindQueue(queue, exchange, '');
|
|
297
306
|
}
|
|
298
|
-
async setupQueue(queueName, options) {
|
|
307
|
+
async setupQueue(queueName, connectionPurpose, options) {
|
|
299
308
|
let queue;
|
|
300
309
|
try {
|
|
301
|
-
const channel = await this.assertChannel();
|
|
310
|
+
const channel = await this.assertChannel({ connectionPurpose });
|
|
302
311
|
debug('assertQueue->channel.addSetup', { queueName });
|
|
303
312
|
await channel.addSetup((setupChannel) => setupChannel.assertQueue(queueName, options));
|
|
304
313
|
debug('assertQueue->channel.assertQueue', { queueName });
|
|
@@ -308,8 +317,8 @@ class RabbitMq {
|
|
|
308
317
|
logger_1.default.error('rabbit: assertQueue error', { queueName, options, error: e });
|
|
309
318
|
if (!this.options?.dontRetryAssert) {
|
|
310
319
|
debug('retrying assertQueue', { queueName });
|
|
311
|
-
const channel = await this.assertChannel({ force: true });
|
|
312
|
-
await this.deleteQueue(queueName);
|
|
320
|
+
const channel = await this.assertChannel({ force: true, connectionPurpose });
|
|
321
|
+
await this.deleteQueue(queueName, connectionPurpose);
|
|
313
322
|
debug('retrying assertQueue->channel.addSetup', { queueName });
|
|
314
323
|
await channel.addSetup((setupChannel) => setupChannel.assertQueue(queueName, options));
|
|
315
324
|
debug('retrying assertQueue->channel.assertQueue', { queueName });
|
|
@@ -322,7 +331,7 @@ class RabbitMq {
|
|
|
322
331
|
this.queues[queueName] = queueName;
|
|
323
332
|
return queue;
|
|
324
333
|
}
|
|
325
|
-
async assertQueue(queueName, options) {
|
|
334
|
+
async assertQueue(queueName, connectionPurpose, options) {
|
|
326
335
|
RabbitMq.validateName('queue', queueName);
|
|
327
336
|
if (this.queues[queueName]) {
|
|
328
337
|
delete this.queueSetupPromises[queueName];
|
|
@@ -331,7 +340,7 @@ class RabbitMq {
|
|
|
331
340
|
if (this.queueSetupPromises[queueName]) {
|
|
332
341
|
return this.queueSetupPromises[queueName];
|
|
333
342
|
}
|
|
334
|
-
this.queueSetupPromises[queueName] = this.setupQueue(queueName, options);
|
|
343
|
+
this.queueSetupPromises[queueName] = this.setupQueue(queueName, connectionPurpose, options);
|
|
335
344
|
return this.queueSetupPromises[queueName];
|
|
336
345
|
}
|
|
337
346
|
saveConsumer(queue, callback, options) {
|
|
@@ -374,11 +383,10 @@ class RabbitMq {
|
|
|
374
383
|
}
|
|
375
384
|
logger_1.default.info(`rabbit: Consuming with lock from queue ${queue} with lockTimeout: ${lockTimeout}ms`);
|
|
376
385
|
}
|
|
377
|
-
const channel = await this.getNewChannel({});
|
|
386
|
+
const channel = await this.getNewChannel({ connectionPurpose: types_1.ConnectionPurpose.Consume });
|
|
378
387
|
return channel.addSetup(async (confirmChannel) => {
|
|
379
388
|
await confirmChannel.assertQueue(queue, options ? { messageTtl: options.messageTtl } : undefined);
|
|
380
389
|
await confirmChannel.prefetch(limit, true);
|
|
381
|
-
const channelConsumerOptions = optionsWithDefaults.channelConsumeOptions ? (0, lodash_1.merge)(types_1.CONSUMER_DEFAULT_OPTIONS, optionsWithDefaults.channelConsumeOptions) : types_1.CONSUMER_DEFAULT_OPTIONS;
|
|
382
390
|
const { consumerTag } = await confirmChannel.consume(queue, async (msg) => {
|
|
383
391
|
if (!msg) {
|
|
384
392
|
return null;
|
|
@@ -439,7 +447,7 @@ class RabbitMq {
|
|
|
439
447
|
catch (e) {
|
|
440
448
|
await localNack(msg);
|
|
441
449
|
}
|
|
442
|
-
},
|
|
450
|
+
}, types_1.CONSUMER_DEFAULT_OPTIONS);
|
|
443
451
|
if (!consumerTag) {
|
|
444
452
|
logger_1.default.error(`rabbit: failed to consume from queue ${queue}`);
|
|
445
453
|
}
|
|
@@ -455,7 +463,7 @@ class RabbitMq {
|
|
|
455
463
|
RabbitMq.validateName('queue', queue);
|
|
456
464
|
const { limit, deadMessageTtl } = optionsWithDefaults;
|
|
457
465
|
await this.saveConsumer(queue, callback, options);
|
|
458
|
-
const channel = await this.getNewChannel({ name: `consume-exchange
|
|
466
|
+
const channel = await this.getNewChannel({ name: `consume - exchange - ${exchange} -queue - ${queue}`, connectionPurpose: types_1.ConnectionPurpose.Consume });
|
|
459
467
|
return channel.addSetup(async (c) => {
|
|
460
468
|
const assertExchange = await (0, utils_1.assertExchangeFanout)(c, exchange);
|
|
461
469
|
await c.assertQueue(queue);
|
|
@@ -467,17 +475,17 @@ class RabbitMq {
|
|
|
467
475
|
]);
|
|
468
476
|
});
|
|
469
477
|
}
|
|
470
|
-
async publish(exchange, content, customHeaders
|
|
478
|
+
async publish(exchange, content, customHeaders) {
|
|
471
479
|
return (0, utils_1.wrapSetImmediate)(async () => {
|
|
472
480
|
RabbitMq.validateName('exchange', exchange);
|
|
473
|
-
const channel = await this.assertChannel({
|
|
474
|
-
await this.assertExchange(exchange);
|
|
481
|
+
const channel = await this.assertChannel({ connectionPurpose: types_1.ConnectionPurpose.Publish });
|
|
482
|
+
await this.assertExchange(exchange, { connectionPurpose: types_1.ConnectionPurpose.Publish });
|
|
475
483
|
await channel.publish(exchange, '', Buffer.from(JSON.stringify(content)), RabbitMq.getPublishOptions(customHeaders));
|
|
476
484
|
});
|
|
477
485
|
}
|
|
478
486
|
async sendToQueue(queue, content, options, customHeaders) {
|
|
479
487
|
try {
|
|
480
|
-
await this.assertChannel({
|
|
488
|
+
await this.assertChannel({ connectionPurpose: types_1.ConnectionPurpose.Publish });
|
|
481
489
|
}
|
|
482
490
|
catch (e) {
|
|
483
491
|
logger_1.default.error(`rabbit sendToQueue: failed to send assert channel when sending to queue ${queue}`, { e });
|
|
@@ -485,7 +493,7 @@ class RabbitMq {
|
|
|
485
493
|
}
|
|
486
494
|
try {
|
|
487
495
|
RabbitMq.validateName('queue', queue);
|
|
488
|
-
await this.assertQueue(queue, options);
|
|
496
|
+
await this.assertQueue(queue, types_1.ConnectionPurpose.Publish, options);
|
|
489
497
|
}
|
|
490
498
|
catch (e) {
|
|
491
499
|
logger_1.default.error(`rabbit sendToQueue: failed to assert queue ${queue}`, { e });
|
|
@@ -497,19 +505,23 @@ class RabbitMq {
|
|
|
497
505
|
return res;
|
|
498
506
|
}
|
|
499
507
|
catch (e) {
|
|
500
|
-
const isConnected = await this.isConnected();
|
|
508
|
+
const isConnected = await this.isConnected(types_1.ConnectionPurpose.Publish);
|
|
501
509
|
logger_1.default.error(`rabbit sendToQueue: failed to send to queue ${queue}, isConnected: ${isConnected}`, { e });
|
|
502
510
|
throw e;
|
|
503
511
|
}
|
|
504
512
|
}
|
|
505
|
-
async isConnected() {
|
|
506
|
-
const connection = await this.getConnection();
|
|
513
|
+
async isConnected(connectionPurpose) {
|
|
514
|
+
const connection = await this.getConnection(connectionPurpose);
|
|
515
|
+
if (!connection) {
|
|
516
|
+
logger_1.default.error('rabbit: isConnected - false');
|
|
517
|
+
return false;
|
|
518
|
+
}
|
|
507
519
|
const isConnected = connection.isConnected();
|
|
508
520
|
if (!isConnected) {
|
|
509
521
|
logger_1.default.error('rabbit: isConnected - false');
|
|
510
522
|
return false;
|
|
511
523
|
}
|
|
512
|
-
const channel = await this.assertChannel();
|
|
524
|
+
const channel = await this.assertChannel({ connectionPurpose });
|
|
513
525
|
try {
|
|
514
526
|
await Promise.all([
|
|
515
527
|
channel.waitForConnect(),
|
package/dist/lib/types.d.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { AmqpConnectionManager } from 'amqp-connection-manager';
|
|
2
2
|
import { ConsumeMessage, Options, Replies } from 'amqplib';
|
|
3
3
|
export interface ExchangesCache {
|
|
4
4
|
[key: string]: any;
|
|
@@ -23,25 +23,27 @@ export interface ConsumeOptions {
|
|
|
23
23
|
useConsumeWithLock?: boolean;
|
|
24
24
|
auditContext?: any;
|
|
25
25
|
enableRabbitTrace?: boolean;
|
|
26
|
-
channelConsumeOptions?: Options.Consume;
|
|
27
26
|
}
|
|
28
27
|
export type CallbackFunction = (msg: ConsumeMessage, ack: any, nack: any) => Promise<any>;
|
|
29
28
|
export type newChannelOpts = {
|
|
30
29
|
name?: string;
|
|
31
30
|
onClose?: null | ((args: any | null) => void);
|
|
32
|
-
options?: CreateChannelOpts | undefined;
|
|
33
31
|
};
|
|
34
32
|
export type assertChannelOpts = {
|
|
35
33
|
channelName?: string;
|
|
36
34
|
force?: boolean;
|
|
37
|
-
newChannelOptions?: newChannelOpts;
|
|
38
35
|
};
|
|
39
|
-
export interface PublishOptions {
|
|
40
|
-
newChannelOptions?: newChannelOpts;
|
|
41
|
-
}
|
|
42
36
|
export type AfConsumer = {
|
|
43
37
|
queue: string;
|
|
44
38
|
callback: CallbackFunction;
|
|
45
39
|
options: ConsumeOptions | undefined;
|
|
46
40
|
};
|
|
47
41
|
export declare const CONSUMER_DEFAULT_OPTIONS: Options.Consume;
|
|
42
|
+
export type ConnectionData = {
|
|
43
|
+
connection: AmqpConnectionManager | null;
|
|
44
|
+
creatingConnection: boolean;
|
|
45
|
+
};
|
|
46
|
+
export declare enum ConnectionPurpose {
|
|
47
|
+
Consume = "consume",
|
|
48
|
+
Publish = "publish"
|
|
49
|
+
}
|
package/dist/lib/types.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.CONSUMER_DEFAULT_OPTIONS = void 0;
|
|
3
|
+
exports.ConnectionPurpose = exports.CONSUMER_DEFAULT_OPTIONS = void 0;
|
|
4
4
|
const HA_PROMOTE_ON_FAILURE = 'ha-promote-on-failure';
|
|
5
5
|
const HA_PROMOTE_ON_SHUTDOWN = 'ha-promote-on-shutdown';
|
|
6
6
|
exports.CONSUMER_DEFAULT_OPTIONS = {
|
|
@@ -9,3 +9,8 @@ exports.CONSUMER_DEFAULT_OPTIONS = {
|
|
|
9
9
|
[HA_PROMOTE_ON_SHUTDOWN]: 'always',
|
|
10
10
|
},
|
|
11
11
|
};
|
|
12
|
+
var ConnectionPurpose;
|
|
13
|
+
(function (ConnectionPurpose) {
|
|
14
|
+
ConnectionPurpose["Consume"] = "consume";
|
|
15
|
+
ConnectionPurpose["Publish"] = "publish";
|
|
16
|
+
})(ConnectionPurpose = exports.ConnectionPurpose || (exports.ConnectionPurpose = {}));
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@autofleet/rabbit",
|
|
3
|
-
"version": "3.2.
|
|
3
|
+
"version": "3.2.22-beta.0",
|
|
4
4
|
"description": "",
|
|
5
5
|
"main": "dist/index.js",
|
|
6
6
|
"scripts": {
|
|
@@ -19,7 +19,6 @@
|
|
|
19
19
|
"amqp-connection-manager": "4.1.9",
|
|
20
20
|
"amqplib": "0.10.3",
|
|
21
21
|
"bluebird": "^3.7.2",
|
|
22
|
-
"lodash": "^4.17.21",
|
|
23
22
|
"moment": "^2.29.1",
|
|
24
23
|
"redis": "^3.1.2",
|
|
25
24
|
"redis-lock": "^0.1.4"
|
|
@@ -31,7 +30,6 @@
|
|
|
31
30
|
"@autofleet/logger": "4.0.6",
|
|
32
31
|
"@types/amqplib": "0.8.2",
|
|
33
32
|
"@types/jest": "^29.5.12",
|
|
34
|
-
"@types/lodash": "^4.17.12",
|
|
35
33
|
"@types/node": "^20.14.11",
|
|
36
34
|
"@typescript-eslint/eslint-plugin": "^4.8.1",
|
|
37
35
|
"eslint": "^7.13.0",
|
|
@@ -45,4 +43,4 @@
|
|
|
45
43
|
},
|
|
46
44
|
"author": "",
|
|
47
45
|
"license": "ISC"
|
|
48
|
-
}
|
|
46
|
+
}
|
package/src/index.ts
CHANGED
|
@@ -2,11 +2,10 @@
|
|
|
2
2
|
|
|
3
3
|
import { EventEmitter, once } from 'events';
|
|
4
4
|
import { promisify } from 'util';
|
|
5
|
-
import { merge } from 'lodash';
|
|
6
5
|
import moment from 'moment';
|
|
7
6
|
import RedisLock from 'redis-lock';
|
|
8
7
|
import {
|
|
9
|
-
AmqpConnectionManager, ChannelWrapper, connect,
|
|
8
|
+
AmqpConnectionManager, ChannelWrapper, connect, CreateChannelOpts,
|
|
10
9
|
} from 'amqp-connection-manager';
|
|
11
10
|
import {
|
|
12
11
|
ConfirmChannel, ConsumeMessage, Options, Replies,
|
|
@@ -37,16 +36,14 @@ import {
|
|
|
37
36
|
QueuesCache,
|
|
38
37
|
RedisLockType,
|
|
39
38
|
ExchangesCache, CONSUMER_DEFAULT_OPTIONS, QueueSetupPromisesDictionary,
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
PublishOptions,
|
|
39
|
+
ConnectionPurpose,
|
|
40
|
+
ConnectionData,
|
|
43
41
|
} from './lib/types';
|
|
44
42
|
|
|
45
43
|
// const debug = nodeDebug('af-rabbitmq')
|
|
46
44
|
const debug = logger.debug.bind(logger);
|
|
47
45
|
|
|
48
46
|
const PUBLISH_TIMEOUT = 1000 * 10;
|
|
49
|
-
|
|
50
47
|
export interface IAfRabbitMq {
|
|
51
48
|
ack: any;
|
|
52
49
|
nack: any;
|
|
@@ -82,6 +79,19 @@ export interface AfRabbitOptions {
|
|
|
82
79
|
rabbitHost?: string;
|
|
83
80
|
}
|
|
84
81
|
|
|
82
|
+
type newChannelOpts = {
|
|
83
|
+
name?: string;
|
|
84
|
+
onClose?: null | ((args: any | null) => void);
|
|
85
|
+
options?: CreateChannelOpts | undefined;
|
|
86
|
+
connectionPurpose: ConnectionPurpose,
|
|
87
|
+
};
|
|
88
|
+
|
|
89
|
+
type assertChannelOpts = {
|
|
90
|
+
channelName?: string;
|
|
91
|
+
force?: boolean;
|
|
92
|
+
connectionPurpose: ConnectionPurpose;
|
|
93
|
+
}
|
|
94
|
+
|
|
85
95
|
type AfConsumer = {
|
|
86
96
|
queue: string;
|
|
87
97
|
callback: CallbackFunction;
|
|
@@ -91,7 +101,7 @@ type AfConsumer = {
|
|
|
91
101
|
const HEARTBEAT = '60';
|
|
92
102
|
|
|
93
103
|
class RabbitMq implements IAfRabbitMq {
|
|
94
|
-
static parseMsg(msg: any)
|
|
104
|
+
static parseMsg(msg: any): any {
|
|
95
105
|
let { content } = msg;
|
|
96
106
|
content = content.toString();
|
|
97
107
|
|
|
@@ -136,14 +146,15 @@ class RabbitMq implements IAfRabbitMq {
|
|
|
136
146
|
|
|
137
147
|
publishChannelSetupPromise: Promise<ChannelWrapper> | null;
|
|
138
148
|
|
|
139
|
-
blockReconnect: boolean | null | undefined
|
|
149
|
+
blockReconnect: boolean | null | undefined;
|
|
140
150
|
|
|
141
|
-
|
|
151
|
+
connectionsMap: {
|
|
152
|
+
[ConnectionPurpose.Consume]: ConnectionData;
|
|
153
|
+
[ConnectionPurpose.Publish]: ConnectionData;
|
|
154
|
+
};
|
|
142
155
|
|
|
143
156
|
em: EventEmitter;
|
|
144
157
|
|
|
145
|
-
creatingConnection: boolean;
|
|
146
|
-
|
|
147
158
|
exchanges: ExchangesCache;
|
|
148
159
|
|
|
149
160
|
queues: QueuesCache;
|
|
@@ -165,8 +176,10 @@ class RabbitMq implements IAfRabbitMq {
|
|
|
165
176
|
this.em = new EventEmitter();
|
|
166
177
|
this.channel = null;
|
|
167
178
|
this.publishChannelSetupPromise = null;
|
|
168
|
-
this.
|
|
169
|
-
|
|
179
|
+
this.connectionsMap = {
|
|
180
|
+
[ConnectionPurpose.Consume]: { connection: null, creatingConnection: false },
|
|
181
|
+
[ConnectionPurpose.Publish]: { connection: null, creatingConnection: false },
|
|
182
|
+
};
|
|
170
183
|
this.exchanges = {};
|
|
171
184
|
this.queues = {};
|
|
172
185
|
this.queueSetupPromises = {};
|
|
@@ -202,7 +215,7 @@ class RabbitMq implements IAfRabbitMq {
|
|
|
202
215
|
return false;
|
|
203
216
|
}
|
|
204
217
|
|
|
205
|
-
public ack = (channel: ConfirmChannel, msg: ConsumeMessageOrNull, shouldUpdateRedisTimestamp = false, releaseLock = null) => async (userMsg: ConsumeMessage)
|
|
218
|
+
public ack = (channel: ConfirmChannel, msg: ConsumeMessageOrNull, shouldUpdateRedisTimestamp = false, releaseLock = null) => async (userMsg: ConsumeMessage): Promise<any> => {
|
|
206
219
|
if (msg) {
|
|
207
220
|
debug('rabbit acking message', { deliveryTag: msg.fields.deliveryTag });
|
|
208
221
|
await channel.ack(msg);
|
|
@@ -228,8 +241,8 @@ class RabbitMq implements IAfRabbitMq {
|
|
|
228
241
|
userMsg: ConsumeMessageOrNull,
|
|
229
242
|
{
|
|
230
243
|
skipRetry = false,
|
|
231
|
-
}: NackOptions = {
|
|
232
|
-
)
|
|
244
|
+
}: NackOptions = {},
|
|
245
|
+
): Promise<any> => {
|
|
233
246
|
await this.unlockRedisIfNeeded(releaseLock);
|
|
234
247
|
if (channel && msg) {
|
|
235
248
|
if (
|
|
@@ -263,30 +276,31 @@ class RabbitMq implements IAfRabbitMq {
|
|
|
263
276
|
}
|
|
264
277
|
}
|
|
265
278
|
|
|
266
|
-
async getConnection() {
|
|
267
|
-
return new Promise<AmqpConnectionManager>(async (resolve, reject) => {
|
|
279
|
+
async getConnection(connectionPurpose: ConnectionPurpose) {
|
|
280
|
+
return new Promise<AmqpConnectionManager | undefined | null>(async (resolve, reject) => {
|
|
281
|
+
const { connection, creatingConnection } = this.connectionsMap[connectionPurpose];
|
|
268
282
|
if (this.blockReconnect) {
|
|
269
283
|
debug('rabbit: block reconnect');
|
|
270
284
|
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
|
|
271
285
|
// @ts-ignore
|
|
272
286
|
return resolve();
|
|
273
287
|
}
|
|
274
|
-
if (
|
|
275
|
-
if (this.options?.disableReconnect ||
|
|
288
|
+
if (connection !== null) {
|
|
289
|
+
if (this.options?.disableReconnect || connection?.isConnected()) {
|
|
276
290
|
debug('rabbit: connection - is connected');
|
|
277
291
|
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
|
|
278
292
|
// @ts-ignore
|
|
279
|
-
return resolve(
|
|
293
|
+
return resolve(connection);
|
|
280
294
|
}
|
|
281
295
|
debug('rabbit: connection - reconnecting');
|
|
282
296
|
}
|
|
283
|
-
if (
|
|
297
|
+
if (creatingConnection) {
|
|
284
298
|
debug('rabbit: creating connection emi');
|
|
285
299
|
this.em.once(CONNECTION_CREATED_CONST, resolve);
|
|
286
300
|
this.em.once(CONNECTION_FAILED_CONST, reject);
|
|
287
301
|
return;
|
|
288
302
|
}
|
|
289
|
-
this.creatingConnection = true;
|
|
303
|
+
this.connectionsMap[connectionPurpose].creatingConnection = true;
|
|
290
304
|
let isResolved = false;
|
|
291
305
|
|
|
292
306
|
// It is import to use it as a function and not as a variable
|
|
@@ -303,12 +317,18 @@ class RabbitMq implements IAfRabbitMq {
|
|
|
303
317
|
};
|
|
304
318
|
|
|
305
319
|
const defaultUrls = findServers();
|
|
306
|
-
const
|
|
320
|
+
const newConnection: AmqpConnectionManager = await connect(defaultUrls, {
|
|
307
321
|
findServers,
|
|
308
322
|
});
|
|
309
323
|
|
|
310
|
-
|
|
311
|
-
|
|
324
|
+
if (!newConnection) {
|
|
325
|
+
logger.error('rabbit: couldnt create a connection');
|
|
326
|
+
return resolve(connection);
|
|
327
|
+
}
|
|
328
|
+
|
|
329
|
+
this.connectionsMap[connectionPurpose].connection = newConnection;
|
|
330
|
+
|
|
331
|
+
newConnection.on('error', (err) => {
|
|
312
332
|
logger.error('rabbit: connection error', { err });
|
|
313
333
|
if (!isResolved) {
|
|
314
334
|
isResolved = true;
|
|
@@ -317,7 +337,7 @@ class RabbitMq implements IAfRabbitMq {
|
|
|
317
337
|
}
|
|
318
338
|
});
|
|
319
339
|
|
|
320
|
-
|
|
340
|
+
newConnection.on('connectFailed', (err) => {
|
|
321
341
|
this.consumersTags = [];
|
|
322
342
|
logger.error('rabbit: connection connectFailed', { err });
|
|
323
343
|
if (!isResolved) {
|
|
@@ -327,8 +347,7 @@ class RabbitMq implements IAfRabbitMq {
|
|
|
327
347
|
}
|
|
328
348
|
});
|
|
329
349
|
|
|
330
|
-
|
|
331
|
-
// this.channel = null;
|
|
350
|
+
newConnection.on('disconnect', ({ err }) => {
|
|
332
351
|
this.consumersTags = [];
|
|
333
352
|
debug('rabbit: connection closed');
|
|
334
353
|
if (this.options?.disableReconnect) {
|
|
@@ -338,25 +357,29 @@ class RabbitMq implements IAfRabbitMq {
|
|
|
338
357
|
logger.error(`${this.RECONNECT_MSG}${err && ` - ${err}`}`);
|
|
339
358
|
}
|
|
340
359
|
});
|
|
341
|
-
|
|
342
|
-
this.connection.once('connect', async () => {
|
|
360
|
+
newConnection.once('connect', async () => {
|
|
343
361
|
debug('rabbit: connection established');
|
|
344
|
-
this.creatingConnection = false;
|
|
345
|
-
this.em.emit(CONNECTION_CREATED_CONST,
|
|
362
|
+
this.connectionsMap[connectionPurpose].creatingConnection = false;
|
|
363
|
+
this.em.emit(CONNECTION_CREATED_CONST, newConnection);
|
|
346
364
|
isResolved = true;
|
|
347
|
-
resolve(
|
|
365
|
+
resolve(newConnection);
|
|
348
366
|
});
|
|
349
367
|
});
|
|
350
368
|
}
|
|
351
369
|
|
|
352
|
-
async getNewChannel({
|
|
353
|
-
|
|
370
|
+
async getNewChannel({
|
|
371
|
+
name = rand().toString(), onClose = null, options = {}, connectionPurpose = ConnectionPurpose.Consume,
|
|
372
|
+
}: newChannelOpts): Promise<ChannelWrapper> {
|
|
373
|
+
let connection!: AmqpConnectionManager | undefined | null;
|
|
354
374
|
try {
|
|
355
|
-
connection = await this.getConnection();
|
|
375
|
+
connection = await this.getConnection(connectionPurpose);
|
|
356
376
|
} catch (e) {
|
|
357
377
|
logger.error(`rabbit: error on get connection for new channel ${name} `, { e });
|
|
358
378
|
throw e;
|
|
359
379
|
}
|
|
380
|
+
if (!connection) {
|
|
381
|
+
throw new Error(`rabbit: couldnt get connection for new channel ${name}`);
|
|
382
|
+
}
|
|
360
383
|
const channel = connection.createChannel({ ...options });
|
|
361
384
|
once(channel, 'close').then((args) => {
|
|
362
385
|
logger.error(`rabbit: channel ${name} closed`);
|
|
@@ -372,7 +395,7 @@ class RabbitMq implements IAfRabbitMq {
|
|
|
372
395
|
}
|
|
373
396
|
}
|
|
374
397
|
|
|
375
|
-
async assertChannel({ force = false,
|
|
398
|
+
async assertChannel({ force = false, connectionPurpose = ConnectionPurpose.Consume }: assertChannelOpts): Promise<ChannelWrapper> {
|
|
376
399
|
if (!this.publishChannelSetupPromise) {
|
|
377
400
|
this.publishChannelSetupPromise = new Promise<ChannelWrapper>(async (resolve, reject) => {
|
|
378
401
|
if (this.channel && !force) {
|
|
@@ -380,7 +403,7 @@ class RabbitMq implements IAfRabbitMq {
|
|
|
380
403
|
}
|
|
381
404
|
|
|
382
405
|
try {
|
|
383
|
-
const channel = await this.getNewChannel({
|
|
406
|
+
const channel = await this.getNewChannel({ connectionPurpose });
|
|
384
407
|
channel.on('error', (err) => {
|
|
385
408
|
logger.error('rabbit: channel error', { err });
|
|
386
409
|
});
|
|
@@ -394,8 +417,8 @@ class RabbitMq implements IAfRabbitMq {
|
|
|
394
417
|
return this.publishChannelSetupPromise;
|
|
395
418
|
}
|
|
396
419
|
|
|
397
|
-
async assertExchange(exchangeName: string) {
|
|
398
|
-
const channel: ChannelWrapper = await this.assertChannel();
|
|
420
|
+
async assertExchange(exchangeName: string, options?: any) {
|
|
421
|
+
const channel: ChannelWrapper = await this.assertChannel({ connectionPurpose: options.connectionPurpose });
|
|
399
422
|
if (this.exchanges[exchangeName]) {
|
|
400
423
|
return this.exchanges[exchangeName];
|
|
401
424
|
}
|
|
@@ -404,35 +427,36 @@ class RabbitMq implements IAfRabbitMq {
|
|
|
404
427
|
return exchange;
|
|
405
428
|
}
|
|
406
429
|
|
|
407
|
-
async getQueueLength(queue: string) {
|
|
430
|
+
async getQueueLength(queue: string, connectionPurpose: ConnectionPurpose = ConnectionPurpose.Consume): Promise<Replies.AssertQueue> {
|
|
408
431
|
RabbitMq.validateName('queue', queue);
|
|
432
|
+
const { connection } = this.connectionsMap[connectionPurpose];
|
|
409
433
|
const { channel } = this;
|
|
410
434
|
if (!channel) {
|
|
411
435
|
throw new Error('channel is not defined');
|
|
412
436
|
}
|
|
413
|
-
debug('rabbit: getting queue length', { queue, connected:
|
|
437
|
+
debug('rabbit: getting queue length', { queue, connected: connection?.isConnected() });
|
|
414
438
|
return channel?.checkQueue(queue);
|
|
415
439
|
}
|
|
416
440
|
|
|
417
|
-
private async deleteQueue(queue: string) {
|
|
441
|
+
private async deleteQueue(queue: string, connectionPurpose: ConnectionPurpose) {
|
|
418
442
|
RabbitMq.validateName('queue', queue);
|
|
419
|
-
const channel: ChannelWrapper = await this.assertChannel();
|
|
443
|
+
const channel: ChannelWrapper = await this.assertChannel({ connectionPurpose });
|
|
420
444
|
logger.info('rabbit: deleting queue', { queue });
|
|
421
445
|
const deleteQueueRes = await channel.deleteQueue(queue);
|
|
422
446
|
debug('queue deleted', deleteQueueRes);
|
|
423
447
|
return deleteQueueRes;
|
|
424
448
|
}
|
|
425
449
|
|
|
426
|
-
async bindQueue(queue: string, exchange: string) {
|
|
427
|
-
const channel: ChannelWrapper = await this.assertChannel();
|
|
450
|
+
async bindQueue(queue: string, exchange: string, connectionPurpose: ConnectionPurpose) {
|
|
451
|
+
const channel: ChannelWrapper = await this.assertChannel({ connectionPurpose });
|
|
428
452
|
await channel.addSetup((setupChannel: ConfirmChannel) => setupChannel.bindQueue(queue, exchange, ''));
|
|
429
453
|
return channel.bindQueue(queue, exchange, '');
|
|
430
454
|
}
|
|
431
455
|
|
|
432
|
-
async setupQueue(queueName: string, options?: Options.AssertQueue): Promise<Replies.AssertQueue> {
|
|
456
|
+
async setupQueue(queueName: string, connectionPurpose: ConnectionPurpose, options?: Options.AssertQueue): Promise<Replies.AssertQueue> {
|
|
433
457
|
let queue: Replies.AssertQueue;
|
|
434
458
|
try {
|
|
435
|
-
const channel: ChannelWrapper = await this.assertChannel();
|
|
459
|
+
const channel: ChannelWrapper = await this.assertChannel({ connectionPurpose });
|
|
436
460
|
debug('assertQueue->channel.addSetup', { queueName });
|
|
437
461
|
await channel.addSetup((setupChannel: ConfirmChannel) => setupChannel.assertQueue(queueName, options));
|
|
438
462
|
debug('assertQueue->channel.assertQueue', { queueName });
|
|
@@ -441,8 +465,8 @@ class RabbitMq implements IAfRabbitMq {
|
|
|
441
465
|
logger.error('rabbit: assertQueue error', { queueName, options, error: e });
|
|
442
466
|
if (!this.options?.dontRetryAssert) {
|
|
443
467
|
debug('retrying assertQueue', { queueName });
|
|
444
|
-
const channel = await this.assertChannel({ force: true });
|
|
445
|
-
await this.deleteQueue(queueName);
|
|
468
|
+
const channel = await this.assertChannel({ force: true, connectionPurpose });
|
|
469
|
+
await this.deleteQueue(queueName, connectionPurpose);
|
|
446
470
|
|
|
447
471
|
debug('retrying assertQueue->channel.addSetup', { queueName });
|
|
448
472
|
await channel.addSetup((setupChannel: ConfirmChannel) => setupChannel.assertQueue(queueName, options));
|
|
@@ -457,7 +481,7 @@ class RabbitMq implements IAfRabbitMq {
|
|
|
457
481
|
return queue;
|
|
458
482
|
}
|
|
459
483
|
|
|
460
|
-
async assertQueue(queueName: string, options?: Options.AssertQueue) {
|
|
484
|
+
async assertQueue(queueName: string, connectionPurpose: ConnectionPurpose, options?: Options.AssertQueue) {
|
|
461
485
|
RabbitMq.validateName('queue', queueName);
|
|
462
486
|
if (this.queues[queueName]) {
|
|
463
487
|
delete this.queueSetupPromises[queueName];
|
|
@@ -468,12 +492,12 @@ class RabbitMq implements IAfRabbitMq {
|
|
|
468
492
|
return this.queueSetupPromises[queueName];
|
|
469
493
|
}
|
|
470
494
|
|
|
471
|
-
this.queueSetupPromises[queueName] = this.setupQueue(queueName, options);
|
|
495
|
+
this.queueSetupPromises[queueName] = this.setupQueue(queueName, connectionPurpose, options);
|
|
472
496
|
return this.queueSetupPromises[queueName];
|
|
473
497
|
}
|
|
474
498
|
|
|
475
499
|
private saveConsumer(queue: string, callback: CallbackFunction, options: ConsumeOptions | undefined) {
|
|
476
|
-
const isConsumerExist
|
|
500
|
+
const isConsumerExist: boolean = this.consumers.some((consumer) => consumer.queue === queue);
|
|
477
501
|
if (!isConsumerExist) {
|
|
478
502
|
logger.info(`rabbit: consumer: ${queue} saved in consumer array`);
|
|
479
503
|
this.consumers.push({
|
|
@@ -522,11 +546,10 @@ class RabbitMq implements IAfRabbitMq {
|
|
|
522
546
|
}
|
|
523
547
|
logger.info(`rabbit: Consuming with lock from queue ${queue} with lockTimeout: ${lockTimeout}ms`);
|
|
524
548
|
}
|
|
525
|
-
const channel = await this.getNewChannel({});
|
|
549
|
+
const channel = await this.getNewChannel({ connectionPurpose: ConnectionPurpose.Consume });
|
|
526
550
|
return channel.addSetup(async (confirmChannel: ConfirmChannel) => {
|
|
527
551
|
await confirmChannel.assertQueue(queue, options ? { messageTtl: options.messageTtl } : undefined);
|
|
528
552
|
await confirmChannel.prefetch(limit, true);
|
|
529
|
-
const channelConsumerOptions = optionsWithDefaults.channelConsumeOptions ? merge(CONSUMER_DEFAULT_OPTIONS, optionsWithDefaults.channelConsumeOptions) : CONSUMER_DEFAULT_OPTIONS;
|
|
530
553
|
const { consumerTag } = await confirmChannel.consume(
|
|
531
554
|
queue,
|
|
532
555
|
async (msg: ConsumeMessageOrNull) => {
|
|
@@ -598,8 +621,7 @@ class RabbitMq implements IAfRabbitMq {
|
|
|
598
621
|
} catch (e) {
|
|
599
622
|
await localNack(msg);
|
|
600
623
|
}
|
|
601
|
-
},
|
|
602
|
-
channelConsumerOptions,
|
|
624
|
+
}, CONSUMER_DEFAULT_OPTIONS,
|
|
603
625
|
);
|
|
604
626
|
if (!consumerTag) {
|
|
605
627
|
logger.error(`rabbit: failed to consume from queue ${queue}`);
|
|
@@ -610,13 +632,13 @@ class RabbitMq implements IAfRabbitMq {
|
|
|
610
632
|
});
|
|
611
633
|
}
|
|
612
634
|
|
|
613
|
-
async consumeFromExchange(queue: string, exchange: string, callback: CallbackFunction, options?: ConsumeOptions)
|
|
635
|
+
async consumeFromExchange(queue: string, exchange: string, callback: CallbackFunction, options?: ConsumeOptions): Promise<any> {
|
|
614
636
|
const optionsWithDefaults = { ...DEFAULT_OPTIONS, ...options };
|
|
615
637
|
RabbitMq.validateName('exchange', exchange);
|
|
616
638
|
RabbitMq.validateName('queue', queue);
|
|
617
639
|
const { limit, deadMessageTtl } = optionsWithDefaults;
|
|
618
640
|
await this.saveConsumer(queue, callback, options);
|
|
619
|
-
const channel: ChannelWrapper = await this.getNewChannel({ name: `consume-exchange
|
|
641
|
+
const channel: ChannelWrapper = await this.getNewChannel({ name: `consume - exchange - ${exchange} -queue - ${queue}`, connectionPurpose: ConnectionPurpose.Consume });
|
|
620
642
|
|
|
621
643
|
return channel.addSetup(async (c: ConfirmChannel) => {
|
|
622
644
|
const assertExchange = await assertExchangeFanout(c, exchange);
|
|
@@ -634,11 +656,11 @@ class RabbitMq implements IAfRabbitMq {
|
|
|
634
656
|
});
|
|
635
657
|
}
|
|
636
658
|
|
|
637
|
-
async publish(exchange: string, content: any, customHeaders?: any
|
|
659
|
+
async publish(exchange: string, content: any, customHeaders?: any): Promise<boolean> {
|
|
638
660
|
return wrapSetImmediate(async () => {
|
|
639
661
|
RabbitMq.validateName('exchange', exchange);
|
|
640
|
-
const channel: ChannelWrapper = await this.assertChannel({
|
|
641
|
-
await this.assertExchange(exchange);
|
|
662
|
+
const channel: ChannelWrapper = await this.assertChannel({ connectionPurpose: ConnectionPurpose.Publish });
|
|
663
|
+
await this.assertExchange(exchange, { connectionPurpose: ConnectionPurpose.Publish });
|
|
642
664
|
await channel.publish(exchange, '',
|
|
643
665
|
Buffer.from(JSON.stringify(content)),
|
|
644
666
|
RabbitMq.getPublishOptions(customHeaders));
|
|
@@ -652,7 +674,7 @@ class RabbitMq implements IAfRabbitMq {
|
|
|
652
674
|
customHeaders?: any,
|
|
653
675
|
): Promise<boolean | undefined> {
|
|
654
676
|
try {
|
|
655
|
-
await this.assertChannel({
|
|
677
|
+
await this.assertChannel({ connectionPurpose: ConnectionPurpose.Publish });
|
|
656
678
|
} catch (e) {
|
|
657
679
|
logger.error(`rabbit sendToQueue: failed to send assert channel when sending to queue ${queue}`, { e });
|
|
658
680
|
throw e;
|
|
@@ -660,7 +682,7 @@ class RabbitMq implements IAfRabbitMq {
|
|
|
660
682
|
|
|
661
683
|
try {
|
|
662
684
|
RabbitMq.validateName('queue', queue);
|
|
663
|
-
await this.assertQueue(queue, options);
|
|
685
|
+
await this.assertQueue(queue, ConnectionPurpose.Publish, options);
|
|
664
686
|
} catch (e) {
|
|
665
687
|
logger.error(`rabbit sendToQueue: failed to assert queue ${queue}`, { e });
|
|
666
688
|
throw e;
|
|
@@ -673,20 +695,24 @@ class RabbitMq implements IAfRabbitMq {
|
|
|
673
695
|
debug(`rabbit: sending to queue ${queue}`, { res });
|
|
674
696
|
return res;
|
|
675
697
|
} catch (e) {
|
|
676
|
-
const isConnected = await this.isConnected();
|
|
698
|
+
const isConnected = await this.isConnected(ConnectionPurpose.Publish);
|
|
677
699
|
logger.error(`rabbit sendToQueue: failed to send to queue ${queue}, isConnected: ${isConnected}`, { e });
|
|
678
700
|
throw e;
|
|
679
701
|
}
|
|
680
702
|
}
|
|
681
703
|
|
|
682
|
-
async isConnected()
|
|
683
|
-
const connection = await this.getConnection();
|
|
704
|
+
async isConnected(connectionPurpose: ConnectionPurpose): Promise<boolean> {
|
|
705
|
+
const connection = await this.getConnection(connectionPurpose);
|
|
706
|
+
if (!connection) {
|
|
707
|
+
logger.error('rabbit: isConnected - false');
|
|
708
|
+
return false;
|
|
709
|
+
}
|
|
684
710
|
const isConnected = connection.isConnected();
|
|
685
711
|
if (!isConnected) {
|
|
686
712
|
logger.error('rabbit: isConnected - false');
|
|
687
713
|
return false;
|
|
688
714
|
}
|
|
689
|
-
const channel: any = await this.assertChannel();
|
|
715
|
+
const channel: any = await this.assertChannel({ connectionPurpose });
|
|
690
716
|
try {
|
|
691
717
|
await Promise.all([
|
|
692
718
|
channel.waitForConnect(),
|
|
@@ -700,7 +726,7 @@ class RabbitMq implements IAfRabbitMq {
|
|
|
700
726
|
return true;
|
|
701
727
|
}
|
|
702
728
|
|
|
703
|
-
async gracefulShutdown(signal: string)
|
|
729
|
+
async gracefulShutdown(signal: string): Promise<void> {
|
|
704
730
|
const tagsNumber = this.consumersTags.length;
|
|
705
731
|
logger.info(`rabbit: [gracefully-shutdown] received ${signal}! canceling #${tagsNumber} tags...`);
|
|
706
732
|
const cancelTagPromises = this.consumersTags.map(([channel, tag]) => channel.cancel(tag));
|
package/src/lib/types.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { AmqpConnectionManager } from 'amqp-connection-manager';
|
|
2
2
|
import { ConsumeMessage, Options, Replies } from 'amqplib';
|
|
3
3
|
|
|
4
4
|
export interface ExchangesCache {
|
|
@@ -28,7 +28,6 @@ export interface ConsumeOptions {
|
|
|
28
28
|
useConsumeWithLock?: boolean;
|
|
29
29
|
auditContext?: any;
|
|
30
30
|
enableRabbitTrace?: boolean;
|
|
31
|
-
channelConsumeOptions?: Options.Consume;
|
|
32
31
|
}
|
|
33
32
|
|
|
34
33
|
export type CallbackFunction = (msg: ConsumeMessage, ack: any, nack: any) => Promise<any>
|
|
@@ -36,18 +35,11 @@ export type CallbackFunction = (msg: ConsumeMessage, ack: any, nack: any) => Pro
|
|
|
36
35
|
export type newChannelOpts = {
|
|
37
36
|
name?: string;
|
|
38
37
|
onClose?: null | ((args: any | null) => void);
|
|
39
|
-
options?: CreateChannelOpts | undefined;
|
|
40
38
|
};
|
|
41
39
|
export type assertChannelOpts = {
|
|
42
40
|
channelName?: string;
|
|
43
41
|
force?: boolean;
|
|
44
|
-
newChannelOptions?: newChannelOpts;
|
|
45
42
|
}
|
|
46
|
-
|
|
47
|
-
export interface PublishOptions {
|
|
48
|
-
newChannelOptions?: newChannelOpts;
|
|
49
|
-
}
|
|
50
|
-
|
|
51
43
|
export type AfConsumer = {
|
|
52
44
|
queue: string;
|
|
53
45
|
callback: CallbackFunction;
|
|
@@ -64,3 +56,13 @@ export const CONSUMER_DEFAULT_OPTIONS: Options.Consume = {
|
|
|
64
56
|
[HA_PROMOTE_ON_SHUTDOWN]: 'always',
|
|
65
57
|
},
|
|
66
58
|
};
|
|
59
|
+
|
|
60
|
+
export type ConnectionData = {
|
|
61
|
+
connection: AmqpConnectionManager | null;
|
|
62
|
+
creatingConnection: boolean;
|
|
63
|
+
};
|
|
64
|
+
|
|
65
|
+
export enum ConnectionPurpose {
|
|
66
|
+
Consume = 'consume',
|
|
67
|
+
Publish = 'publish',
|
|
68
|
+
}
|