UNPKG

nestjs-rabbitmq-retry

Version:
141 lines 6.63 kB
"use strict"; var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) { var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d; if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc); else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r; return c > 3 && r && Object.defineProperty(target, key, r), r; }; var __metadata = (this && this.__metadata) || function (k, v) { if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v); }; var __param = (this && this.__param) || function (paramIndex, decorator) { return function (target, key) { decorator(target, key, paramIndex); } }; var RabbitMQLoader_1; Object.defineProperty(exports, "__esModule", { value: true }); exports.RabbitMQLoader = void 0; const common_1 = require("@nestjs/common"); const nestjs_discovery_1 = require("@golevelup/nestjs-discovery"); const amqplib_1 = require("amqplib"); const rabbitmq_constants_1 = require("./rabbitmq.constants"); let RabbitMQLoader = exports.RabbitMQLoader = RabbitMQLoader_1 = class RabbitMQLoader { constructor(config, discoveryService) { this.config = config; this.discoveryService = discoveryService; this.logger = new common_1.Logger(RabbitMQLoader_1.name); this.channels = new Map(); } async onModuleInit() { if (this.config.channels.filter((channel) => channel.primary).length === 0) { throw new Error('One of the channels to be created needs to be the primary channel.'); } else { await this.createChannels(); const channels = await this.getChannel(); await this.createQueues(channels[0]); await this.registerListeners(); } } onModuleDestroy() { Promise.all([this.closeChannels(), this.connection.close()]); } async getConnection() { try { const amqp = this.config.isAMQPS ? 'amqps' : 'amqp'; const user = this.config.username; const password = this.config.password; const host = this.config.host; this.connection = await (0, amqplib_1.connect)(`${amqp}://${user}:${password}@${host}`); this.logger.log('Connection with RabbitMQ successfully established'); return this.connection; } catch (error) { this.logger.error('Connection with RabbitMQ cannot be established successfully'); throw error; } } async createChannels() { const connection = await this.getConnection(); for (const item of this.config.channels) { const channels = []; for (let index = 0; index < item.concurrency; index++) { const channel = await connection.createChannel(); await channel.prefetch(item.prefetch); channels.push(channel); } this.channels.set(item.name, channels); } this.logger.log('Channels created successfully'); } async closeChannels() { this.channels.forEach((channels) => { channels.forEach(async (channel) => await channel.close()); }); } async getChannel(name) { const channels = this.channels.get(name); if (channels === undefined) { const name = this.config.channels.find((channel) => channel.primary).name; return this.channels.get(name); } return channels; } async createQueues(channel) { for (const queue of this.config.queues) { await Promise.all([ channel.assertExchange(queue.exchange.name, queue.exchange.type), channel.assertQueue(queue.name, { ...queue.options, deadLetterExchange: queue.exchange.name, deadLetterRoutingKey: `${queue.name}.retry`, }), channel.assertQueue(`${queue.name}.retry`, { messageTtl: queue.ttl, deadLetterExchange: queue.exchange.name, deadLetterRoutingKey: queue.name, }), channel.assertQueue(`${queue.name}.dlq`), channel.bindQueue(queue.name, queue.exchange.name, queue.routingKey), channel.bindQueue(queue.name, queue.exchange.name, queue.name), channel.bindQueue(`${queue.name}.retry`, queue.exchange.name, `${queue.name}.retry`), channel.bindQueue(`${queue.name}.dlq`, queue.exchange.name, `${queue.name}.dlq`), ]); } this.logger.log('Queues created successfully'); } async registerListeners() { const methods = await this.discoveryService.providerMethodsWithMetaAtKey(rabbitmq_constants_1.LISTENER_QUEUE); methods.forEach((method) => { const meta = method.meta; const handler = (...args) => method.discoveredMethod.handler.apply(method.discoveredMethod.parentClass.instance, args); this.listener(meta.queue, meta.channelName, handler); }); } async listener(queue, channelName, callback) { const channels = await this.getChannel(channelName); for (const channel of channels) { channel.consume(queue, async (message) => { try { await callback(message.content.toString('utf8'), message.fields, message.properties); channel.ack(message); } catch (error) { if (message.properties.headers['x-death'] !== undefined && message.properties.headers['x-death'][0].count + 1 >= this.config.retry) { channel.sendToQueue(`${queue}.dlq`, Buffer.from(JSON.stringify(message))); channel.ack(message, false); } else { channel.nack(message, false, false); } } }); } } }; exports.RabbitMQLoader = RabbitMQLoader = RabbitMQLoader_1 = __decorate([ (0, common_1.Injectable)(), __param(0, (0, common_1.Inject)(rabbitmq_constants_1.CONFIG_OPTIONS)), __metadata("design:paramtypes", [Object, nestjs_discovery_1.DiscoveryService]) ], RabbitMQLoader); //# sourceMappingURL=rabbitmq.loader.js.map