onirii
Version:
Universal queue SDK
269 lines (268 loc) • 11.1 kB
JavaScript
;
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;