UNPKG

onirii

Version:

Universal queue SDK

269 lines (268 loc) 11.1 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.AmqpConnectService = void 0; const tslib_1 = require("tslib"); const amqp = (0, tslib_1.__importStar)(require("amqplib")); const log_factory_1 = require("../../factory/log-factory"); const env_loader_util_1 = require("../../util/env-loader-util"); const amqp_original_channel_wrapper_1 = require("../../wrapper/amqp-original-channel-wrapper"); const amqp_original_confirm_channel_wrapper_1 = require("../../wrapper/amqp-original-confirm-channel-wrapper"); const amqp_channel_service_1 = require("./amqp-channel-service"); const amqp_confirm_channel_service_1 = require("./amqp-confirm-channel-service"); /** * Amqp Connect Server For Create Connect * * @since 1.0.0 * @date 2021-06-01 * @author Luminous(BGLuminous) */ class AmqpConnectService { /** * Create a amqp connection service instance * * @param {string} name current server identify name (this name also extend to log config). * @param {string | Options.Connect} amqpUrl specific amqp url and it should include auth info,if is undefined will * load from env. */ constructor(name, amqpUrl) { // current connect amqp server this.currentAmqpServerUrl = ''; // connect max channel default 2048, this value can overwrite at env with MAX_CHANNEL_COUNT this.MAX_CHANNEL_COUNT = 2047; // connect channel pool this.wrapperChannelPool = []; // connect channel service pool this.serviceChannelPool = []; // channel position this.channelPosition = -1; this.instanceName = `${name}-amqp-connect`; // init logger this.logger = log_factory_1.LogFactory.create(this.instanceName); if (amqpUrl) { this.currentAmqpServerUrl = amqpUrl; } // load amqp server url from public env file if (!this.currentAmqpServerUrl) { const loadFromEnv = env_loader_util_1.EnvLoaderUtil.getInstance().getPublicConfig().amqpServerUrl; if (loadFromEnv) { this.currentAmqpServerUrl = loadFromEnv; } } // load max channel count if configured const maxChannelCount = env_loader_util_1.EnvLoaderUtil.getInstance().getPublicConfig().maxChannelCount; if (maxChannelCount && !isNaN(maxChannelCount)) { this.MAX_CHANNEL_COUNT = maxChannelCount; } // check if (!this.currentAmqpServerUrl) { throw new Error('Must Specific Amqp Server Url'); } } /** * Initialize Current Connection, if got connect error throw Error * * @param init init callback after connect succeed * @param retry connect retry times if set -1 this connect will not re-connect * @param options socket custom options */ ready(init, retry = 0, options) { return (0, tslib_1.__awaiter)(this, void 0, void 0, function* () { const retryInfo = retry > 0 ? `Retry: ${retry} times` : ''; this.logger.debug(`Initialize ${this.instanceName} Creating Amqp Server: ${this.currentAmqpServerUrl} ${retryInfo}`); try { this.currentConnection = yield amqp .connect(this.currentAmqpServerUrl, options) .catch((err) => { this.logger.error(`Connection(${this.instanceName}) Connecting Server Got Error: ${err}`); throw new Error(err); }); this.addDefaultListener(); this.logger.debug(`${this.instanceName} Connected Amqp Server`); if (typeof init === 'function') { init(this); } } catch (err) { this.logger.error(`${this.instanceName} Connect Got Error: ${err.stack}`); if (retry >= 0 && env_loader_util_1.EnvLoaderUtil.getInstance().getPublicConfig().autoReconnect) { yield new Promise(r => setTimeout(r, 5 * 1000)); yield this.ready(init, ++retry, options); return; } throw err; } }); } /** * Add default listener for each connect service instance * * @private */ addDefaultListener() { this.addErrorListener((err) => { this.logger.error(`Amqp Connect(${this.instanceName}) Got Error: ${err} ${JSON.stringify(err)}`); }); this.addCloseListener((err) => { this.logger.error(`Amqp Connect(${this.instanceName}) Closed: ${err} ${JSON.stringify(err)}`); }); } /** * add close event listener * * @param process event emit callback */ addCloseListener(process) { var _a; (_a = this.currentConnection) === null || _a === void 0 ? void 0 : _a.on('close', err => { process(err); }); } /** * add error event listener * * @param process event emit callback */ addErrorListener(process) { var _a; (_a = this.currentConnection) === null || _a === void 0 ? void 0 : _a.on('error', err => process(err)); } /** * Kill current channel by name, this also will refresh pool content * * @param {string} channelName channel instance name * @return {Promise<void>} */ killChannel(channelName) { return (0, tslib_1.__awaiter)(this, void 0, void 0, function* () { // kill in service list let targetChannel; targetChannel = this.serviceChannelPool.find(element => element.instanceName === channelName); if (targetChannel) { this.serviceChannelPool = this.serviceChannelPool.filter(element => element.instanceName !== channelName); return true; } targetChannel = this.wrapperChannelPool.find(element => element.instanceName === channelName); if (targetChannel) { yield targetChannel.close(); this.wrapperChannelPool = this.wrapperChannelPool.filter(element => element.instanceName !== channelName); return true; } this.logger.error(`Can't Kill Unknown Channel ${channelName}`); return false; }); } /** * Close connection also will close all channel in this connection * * @return {Promise<void>} */ close(init = true) { var _a; return (0, tslib_1.__awaiter)(this, void 0, void 0, function* () { try { if (!this.currentConnection) { this.logger.warn('Please ready this connection first'); } yield this.killWrapperChannel(); yield this.killServiceChannel(); yield ((_a = this.currentConnection) === null || _a === void 0 ? void 0 : _a.close()); this.logger.warn(`Amqp Server Instance ${this.instanceName} Closed`); } catch (err) { if (!init) { this.logger.warn(`Cant Close Instance ${this.instanceName} Closed`); } } }); } createChannelService(confirmChannel) { return (0, tslib_1.__awaiter)(this, void 0, void 0, function* () { if (!this.currentConnection) { throw new Error('Please ready this connection first'); } // check exist channel count if (this.checkChannelCountOvered()) { return; } // create confirm channel service if (confirmChannel) { const confirmChannelService = new amqp_confirm_channel_service_1.AmqpConfirmChannelService(this.getNextChannelName(), yield this.currentConnection.createConfirmChannel()); this.serviceChannelPool.push(confirmChannelService); return confirmChannelService; } // create channel service const channelService = new amqp_channel_service_1.AmqpChannelService(this.getNextChannelName(), yield this.currentConnection.createChannel()); this.serviceChannelPool.push(channelService); return channelService; }); } createChannelWrapper(confirmChannel) { return (0, tslib_1.__awaiter)(this, void 0, void 0, function* () { if (!this.currentConnection) { throw new Error('Please ready this connection first'); } // check exist channel count if (this.checkChannelCountOvered()) { return; } // create confirm channel wrapper if (confirmChannel) { const confirmChannelInstance = new amqp_original_confirm_channel_wrapper_1.AmqpOriginalConfirmChannelWrapper(yield this.currentConnection.createConfirmChannel(), this.getNextChannelName()); this.wrapperChannelPool.push(confirmChannelInstance); return confirmChannelInstance; } // create channel wrapper const channelInstance = new amqp_original_channel_wrapper_1.AmqpOriginalChannelWrapper(yield this.currentConnection.createChannel(), this.getNextChannelName()); this.wrapperChannelPool.push(channelInstance); return channelInstance; }); } /** * Kill current AmqpConnectService all wrapper channel * @return {Promise<void>} * @private */ killWrapperChannel() { return (0, tslib_1.__awaiter)(this, void 0, void 0, function* () { for (const channelPoolElement of this.wrapperChannelPool) { yield channelPoolElement.channel.close(); this.logger.warn(`Killed Channel ${channelPoolElement.instanceName}`); } }); } /** * KIll current AmqpConnectService all service channel * * @return {Promise<void>} * @private */ killServiceChannel() { return (0, tslib_1.__awaiter)(this, void 0, void 0, function* () { yield Promise.all(this.serviceChannelPool.map(element => element.close())); }); } /** * Check channel count * * @return {Promise<boolean>} * @protected */ checkChannelCountOvered() { if (this.wrapperChannelPool.length + this.serviceChannelPool.length > this.MAX_CHANNEL_COUNT) { this.logger.error(`Can't Create New Channel, Active Channels Count Exceeds Total Connection Supports: MAX:${this.MAX_CHANNEL_COUNT}`); return true; } return false; } /** * Generate Next Channel Name * * @return {string} new channel name * @private -- */ getNextChannelName() { this.channelPosition++; return `${this.instanceName}-channel-${this.channelPosition}`; } } exports.AmqpConnectService = AmqpConnectService;