rabbitmq-herald-client
Version:
Fork of herald-client that use rabbitmq as server to sending messages
932 lines (842 loc) • 33.7 kB
JavaScript
/*
* The MIT License (MIT)
*
* Copyright (c) 2015 (NumminorihSF) Konstantine Petryaev
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
'use strict';
var DEFAULT_RPC_TIMEOUT = 15000;
/**
* RabbitHeraldClient class.
* Класс RabbitHeraldClient
* @class RabbitHeraldClient
* @extends event.EventEmitter
*/
/**
* Constructor.
* Конструктор.
* @method constructor
* @param {Object} [settings] Settings for RabbitHeraldClient.
* Настройки для RabbitHeraldClient.
* @param {Object} [settings.default] Default settings for messaging.
* Настройки для сообщений по умолчанию.
* @param {Boolean} [settings.forceName=false] If `true` - `settings.name` can change name to some value, else use `settings.connect.user`.
* Если `true` - `settings.name` может выставить значение name, иначе, используется `settings.connect.user`.
* @param {String} [settings.name=(settings.forceName?Math.random():settings.connect.user)] Name of application or client.
* Название клиента или приложения.
* @param {String} [settings.uid=this.name+Math.random()] Unique id of application or client.
* Уникальный id приложения или клиента.
* @param {Logger|Object} [settings.logger] Object for log events.
* Объект для логгирования событий.
* @param {Object} [settings.connect='{port:5672, user:'guest', password:'guest'}'] Object of connection properties.
* Объект настроек подключения к серверу.
* @param {CryptMaker|Object} [settings.messageMaker] Object with make and parse logic.
* Объект, реализующий логику создания сообщений и их парсинга.
* @param {String} [needCrypt='no'] If need crypt messages - algorithm for crypt. By default doesn't encrypt.
* Если необходимо шифровать сообщения - алогоритм шифрования. По умолчанию не шифрует.
* @param {String} [key] Encryption key.
* Ключ шифрования.
* @returns {RabbitHeraldClient}
*/
function RabbitHeraldClient(settings, needCrypt, key){
RabbitHeraldClient.super_.call(this);
if (typeof settings === 'string'){
settings = {};
}
settings = settings || {};
this.connectProp = settings.connect || {};
this.connectProp.host = this.connectProp.host || '127.0.0.1';
this.connectProp.port = this.connectProp.port || 5672;
this.connectProp.user = this.connectProp.user || 'guest';
this.connectProp.password = this.connectProp.password || 'guest';
this['default'] = settings.default || RabbitHeraldClient.defaultVars;
this.logger = settings.logger || require(__dirname+'/logger.js').getLogger('H_Client');
if (settings.forceName) this.name = String( (settings.name || settings.iAm || Math.floor(Math.random()*1000000)) );
else this.name = this.connectProp.user;
this.uid = String( (settings.uid || this.name + '_' + Math.floor(Math.random()*1000000)) );
/**
* Connection status. True if connected.
* Статус подключения. True если подключен.
* @type {boolean}
*/
this.connected = false;
var self = this;
this.lastMessageId = 1;
this.lastActionId = 1;
this.messageQueue = [];
this.messageCallback = {};
this.rpcCallback = {};
this.isSocketFree = false;
this.deliveryPublish = !!settings.deliveryPublish;
this.rpcFunction = [];
this.should_work = true;
this.listening = [];
this.on('connect', function(){
this.connected = true;
}.bind(this));
this.on('close', function(){
this.connected = false;
this.isSocketFree = false;
if (this.should_work) setTimeout(function(){
if (this.should_work) this.connect();
}.bind(this), 1000);
}.bind(this));
this.on('drain', function(){
var mes = self.messageQueue.shift();
if (mes) {
var ready = self.$.write(mes.m, 'utf8', mes.c);
if (ready) process.nextTick(function(){
self.emit('drain')
});
}
else self.isSocketFree = true;
});
if (!settings.logger) this.logger.warn('No logger defined. Use default (it spawns too much messages).');
return this;
}
/*
* Get EventEmitter as prototype.
* Используем EventEmitter в качестве прототипа.
*/
(function(){
require('util').inherits(RabbitHeraldClient, (require('events')).EventEmitter);
})();
/**
* Default variables for messaging. Переменные по умолчанию для сообщений.
* @static
* @type {{retry: number, timeout: number}}
*/
RabbitHeraldClient.defaultVars = {
retry: 5,
timeout: 1000
};
/**
* Remote procedure calling by name of application.
* Удаленный вызов процедуры у одного из приложений с данным именем.
* @param {string} name Name of application, that you want to rpc.
* Имя приложения, в котором нужно вызвать процедуру.
* @param {Object} action Action object, that you want to call at application.
* Объект действия для удаленного выполнения.
* @param {string} action.name Action name to do
* Название действия для удаленного выполнения.
* @param {string|Object} action.args Arguments for rpc or callback, if no args need to rpc - send `{}`.
* Аргументы для удаленой процедуры. Если не нужны - используйте `{}`.
* @param {Object} [options] Options for rpc.
* Опции для rpc.
* @param {Function} [callback] Callback for rpc. If rpc failed and no callback defined - RabbitHeraldClient will emit error.
* First argument for callback is Error object or null. Second is result.
* Callback для удаленного вызова. Если вызов не удачен и параметр не определен - RabbitHeraldClient примет ошибку.
* Первый аргумент для функции - объект ошибки или null. Второй - результат вызова.
* @returns {Boolean} true can send just now, false if need to send previous messages.
* true если отправленно сразу, false, если необходимо подождать отправки других сообщений.
*/
RabbitHeraldClient.prototype.rpc = function(name, action, options, callback){
if (!callback){
if (typeof options === 'function') {
callback = options;
options = {};
}
else callback = function(err){
this.emit('error', err);
}.bind(this);
}
if (!name || typeof name !== 'string') return callback(new Error('WRONG_ARGS'));
if (!action || typeof action !== 'object') return callback(new Error('WRONG_ARGS'));
if (!action.name || typeof action.name !== 'string') return callback(new Error('WRONG_ARGS'));
if (!('args' in action)) return callback(new Error('WRONG_ARGS'));
this.lastActionId = this.lastActionId || 0;
var header = {
messageId: this.lastActionId++
};
header.replyTo = this.name + '.rpc.res.'+this.uid;
var body = JSON.stringify({
params: action.args,
id: header.messageId,
method: action.name
});
var self = this;
options = options || {};
options.timeout = options.timeout || DEFAULT_RPC_TIMEOUT;
var opts = {
messageId: String(header.messageId),
replyTo: self.name+'.rpc.res.'+self.uid,
userId: self.name,
correlationId: self.uid,
timestamp: new Date(),
expiration: String(options.timeout)
};
this.$.getChannelOnConnect(function(err, chan, done){
if (err) return callback(err);
chan.basic.publish('rpc.bus', name, body, opts, {}, function(err){
done(err);
if (err){
return callback(err);
}
self.rpcCallback[header.messageId] = callback;
self.rpcCallback[header.messageId] = function(err, data){
delete self.rpcCallback[header.messageId];
clearTimeout(timeout);
return callback(err, data);
};
var timeout = setTimeout(function(){
self.rpcCallback[header.messageId] && self.rpcCallback[header.messageId](new Error('RPC_TIMEOUT'));
}, Number(opts.expiration));
})
})
};
/**
* Remote stateless procedure calling by name of application.
* Удаленный вызов процедуры у одного из приложений с данным именем с отрывом от состояния.
* @param {string} name Name of application, that you want to rpc.
* Имя приложения, в котором нужно вызвать процедуру.
* @param {Object} action Action object, that you want to call at application.
* Объект действия для удаленного выполнения.
* @param {string} action.name Action name to do
* Название действия для удаленного выполнения.
* @param {string|Object} action.args Arguments for rpc or callback, if no args need to rpc - send `{}`.
* Аргументы для удаленой процедуры. Если не нужны - используйте `{}`.
* @param {Object} [options] Options for rpc.
* Опции для rpc.
* @param {Function} [callback] Callback for rpc. If rpc failed and no callback defined - RabbitHeraldClient will emit error.
* First argument for callback is Error object or null. Second is result.
* Callback для удаленного вызова. Если вызов не удачен и параметр не определен - RabbitHeraldClient примет ошибку.
* Первый аргумент для функции - объект ошибки или null. Второй - результат вызова.
* @returns {Boolean} true can send just now, false if need to send previous messages.
* true если отправленно сразу, false, если необходимо подождать отправки других сообщений.
*/
RabbitHeraldClient.prototype.rpcStateless = function(name, action, options, callback){
if (!callback){
if (typeof options === 'function') {
callback = options;
options = {};
}
else callback = function(err){
this.emit('error', err);
}.bind(this);
}
if (!name || typeof name !== 'string') return callback(new Error('WRONG_ARGS'));
if (!action || typeof action !== 'object') return callback(new Error('WRONG_ARGS'));
if (!action.name || typeof action.name !== 'string') return callback(new Error('WRONG_ARGS'));
if (!('args' in action)) return callback(new Error('WRONG_ARGS'));
this.lastActionId = this.lastActionId || 0;
var header = {
messageId: this.lastActionId++
};
header.replyTo = this.name + '.rpc.res.'+this.uid;
var body = JSON.stringify({
params: action.args,
id: header.messageId,
method: action.name
});
var self = this;
options = options || {};
options.timeout = options.timeout || DEFAULT_RPC_TIMEOUT;
var opts = {
messageId: String(header.messageId),
replyTo: self.name+'.rpc.res',
userId: self.name,
correlationId: self.uid,
timestamp: new Date(),
expiration: String(options.timeout)
};
this.$.getChannelOnConnect(function(err, chan, done){
if (err) return callback(err);
chan.basic.publish('rpc.bus', name, body, opts, {}, function(err){
done(err);
if (err){
return callback(err);
}
self.rpcCallback[header.messageId] = callback;
self.rpcCallback[header.messageId] = function(err, data){
delete self.rpcCallback[header.messageId];
clearTimeout(timeout);
return callback(err, data);
};
var timeout = setTimeout(function(){
self.rpcCallback[header.messageId] && self.rpcCallback[header.messageId](new Error('RPC_TIMEOUT'));
}, Number(opts.expiration));
})
})
};
/**
* Find rpc call and use
* @param {string} action
* @param {Object} headerPart
* @param {Object} body
* @param {Object} body.params
* @param {Number} timeout
* @param {Function} callback
* @returns {*}
* @private
*/
RabbitHeraldClient.prototype._doRpc = function(action, headerPart, body, timeout, callback){
if (!body || !body.params) return callback(new Error('WRONG_ARGS'));
if (!this.rpcFunction[action]) return callback(new Error('WRONG_ACTION'));
var count = 0;
var timer = setTimeout(function(){
cb(new Error('CAN_NOT_DO_IT_TIME'));
}, timeout);
function cb (err, result){
if (count++) console.warn('down call callback', new Error().stack);
clearTimeout(timer);
callback(err, result);
}
if (this.rpcFunction[action].length === 2) return this.rpcFunction[action](body.params, cb);
return this.rpcFunction[action](headerPart, body.params, cb);
};
/**
* Function for remote calling. It is callback for {@link RabbitHeraldClient#addRpcWorker} method.
* If function has 2 arguments, caller will not send to it.
* Функция для удаленного вызова. Передается как callback в метод {@link RabbitHeraldClient#addRpcWorker}.
* Если у функции 2 аргумента - caller не будет передан.
* @method remoteProcedure
* @member RabbitHeraldClient
* @param {Object} [caller] UID and name of application that called this.
* UID и название приложения, вызвавшего функцию.
* @param {Object} args Arguments for call. Аргументы для вызова.
* @param {Function} callback Callback function to return result. First arg is Error object or null. Second response data if is.
* Функция для возврата результатов. Первый аргумент - объект ошибки или null. Второй - результат.
*/
/**
* Add function to work with rpc calls.
* Добавляет функцию для удаленного использования.
* @param {String} actionName Action name. Название действия.
* @param {Function} callback Function to call. {@link RabbitHeraldClient#remoteProcedure Should be like this}.
* Вызываемая функция. {@link RabbitHeraldClient#remoteProcedure Должна соответствовать этому шаблону}.
* @returns {Boolean} True if added. false if was function with such name.
* False means, that you should remove old rpc function with such actionName.
* True если добавленно. false если есть функция с таким именем.
* False означает, что Вам необходимо удалить старую функцию с таким названием действия.
*/
RabbitHeraldClient.prototype.addRpcWorker = function(actionName, callback){
if (this.rpcFunction[actionName]) return false;
this.rpcFunction[actionName] = callback;
return true;
};
/**
* Remove rpc function. Удаляет функцию из используемых.
* @param {String} actionName Action name. Название действия.
* @returns {Boolean} true if was such function. Else returns false.
* true если такая функция была. false, если нет.
*/
RabbitHeraldClient.prototype.removeRpcWorker = function(actionName){
var answer = actionName in this.rpcFunction;
delete this.rpcFunction[actionName];
return answer;
};
RabbitHeraldClient.prototype._sendError = function(id, dest, errorMessage){
var self = this;
this.$.getChannelOnConnect(function(err, chan, done){
chan.basic.publish('rpc.res.bus', dest, JSON.stringify({id: id, error: errorMessage && errorMessage.message || typeof errorMessage === 'string' ? errorMessage : 'Some_Error'}), {
messageId: id,
userId: self.name
}, {}, function(err){
if (err) console.error('error in send error', err);
});
});
};
/**
* Parse encrypted message and think, what should do with it
* @returns {*}
* @private
*/
RabbitHeraldClient.prototype._parseRpcMessage = function(chan, message, h, headers, args){
var self = this;
args.messageId = args['message-id'];
args.replyTo = args['reply-to'];
args.userId = args['user-id'];
args.correlationId = args['correlation-id'];
if (!headers || !args.userId){
chan.basic.nack(headers['delivery-tag'], {}, function(){});
return this._sendError(args.messageId, args.replyTo, new Error('Need "user-id" header.'));
}
var body;
try{
body = JSON.parse(message.toString('utf-8'));
}
catch(e){
chan.basic.nack(headers['delivery-tag'], {}, function(){});
return this._sendError(args.messageId, args.replyTo, new Error('Need valid message body.'));
}
if (!body || !body.id){
chan.basic.nack(headers['delivery-tag'], {}, function(){});
return;
}
if (!body.method){
chan.basic.nack(headers['delivery-tag'], {}, function(){});
return this._sendError(args.messageId, args.replyTo, new Error('Need "method" field in body.'));
}
if (!this.rpcFunction[body.method]){
chan.basic.nack(headers['delivery-tag'], {}, function(){});
return this._sendError(args.messageId, args.replyTo, new Error('Need available "method" field in body.'));
}
return this._doRpc(body.method, {name: args.userId, uid: args.correlationId}, body, Number(args.timeout) || Number(args.expiration) || 15000, function(error, result){
chan.basic.ack(headers['delivery-tag'], function(){});
self.$.getChannelOnConnect(function(err, chan, done){
if (err) return console.error('Can not send rpc res', err);
var opts = {
messageId: String(args.messageId),
replyTo: self.name+'.rpc.res.'+self.uid,
userId: self.name,
correlationId: self.uid,
timestamp: new Date(),
expiration: String(15000)
};
chan.basic.publish('rpc.res.bus', args.replyTo, JSON.stringify({
error: error && (error.message || error),
result: result,
id: args.messageId
}), opts, {}, function(err){
done(err);
if (err){
console.error('Error in return res', args.messageId);
}
});
});
});
};
/**
* Parse encrypted message and think, what should do with it
* @returns {*}
* @private
*/
RabbitHeraldClient.prototype._parseRpcResMessage = function(chan, message, h, headers, args){
var self = this;
args.messageId = args['message-id'];
args.replyTo = args['reply-to'];
args.userId = args['user-id'];
args.correlationId = args['correlation-id'];
if (!args || !args.userId){
console.error('Undefined user on rpc res');
return chan.basic.nack(headers['delivery-tag'], {}, function(){});
}
var body;
try{
body = JSON.parse(message.toString('utf-8'));
}
catch(e){
console.error('Unparsed rpc res');
return chan.basic.nack(headers['delivery-tag'], {}, function(){});
}
if (!body || !body.id){
return chan.basic.nack(headers['delivery-tag'], {}, function(){});
}
var tmp = false;
if (!this.rpcCallback[args.messageId]){
console.error('Undefined rpc res', args.messageId, body);
return chan.basic.nack(headers['delivery-tag'], {}, function(){});
}
setImmediate(function(){
this.rpcCallback[args.messageId](body.error, body.result);
}.bind(this));
return chan.basic.ack(headers['delivery-tag'], function(){});
};
RabbitHeraldClient.prototype.subscribe = function(){
console.error('.subscribe is not implemented.');
};
/**
* Publish some event.
* Пибликует событие.
* @param {String} event
* @param {Object|String} body
* @param {Function} callback
*/
//RabbitHeraldClient.prototype.publish = function(event, body, callback){
// var self = this;
// this.$.getChannelOnConnect(function(err, chan, done){
// if (err){
// return callback(err);
// }
// var exchange = self._getExchangeName('event');
// var routingKey = self._getRoutingKey('event', event);
// chan.basic.publish(
// exchange,
// routingKey,
// JSON.stringify({
// jsonrpc:'2.0',
// id: self.lastMessageId,
// params: {
// body: body
// }
// }),
// {
// 'delivery_mode':self.deliveryPublish ? 2 : 1,
// messageId:self.lastMessageId++,
// timestamp:new Date()
// },
// {},
// function(err){
// done(err);
// callback(err);
// }
// );
// });
//};
RabbitHeraldClient.prototype._declareExchange = function(callback){
this.$.getChannel(function(err, chan, done) {
if (err) {
return callback(err);
}
chan.exchange.declare('rpc.bus', 'topic', {durable: true}, function (err) {
if (err) {
done(err);
return callback(err);
}
chan.exchange.declare('rpc.res.bus', 'topic', {durable: true}, function (err) {
if (err) {
done(err);
return callback(err);
}
chan.exchange.declare('event.bus', 'topic', {durable: true}, function (err) {
done(err);
return callback(err);
});
});
});
});
};
RabbitHeraldClient.prototype._declareAppQueue = function(callback){
var self = this;
this.$.getChannel(function(err, chan, done) {
if (err) {
return callback(err);
}
chan.queue.declare(self.name+'.rpc.in', {durable: true}, function (err) {
if (err) {
done(err);
return callback(err);
}
chan.queue.declare(self.name+'.rpc.res', {durable: true}, function (err) {
done(err);
return callback(err);
});
});
});
};
RabbitHeraldClient.prototype._declareAppUidQueue = function(callback){
var self = this;
this.$.getChannel(function(err, chan, done) {
if (err) {
return callback(err);
}
chan.queue.declare(self.name+'.rpc.in.'+self.uid, {durable: true, exclusive: true}, function (err) {
if (err) {
done(err);
return callback(err);
}
chan.queue.declare(self.name+'.rpc.res.'+self.uid, {durable: true, exclusive: true}, function (err) {
done(err);
return callback(err);
});
});
});
};
RabbitHeraldClient.prototype._declareAndBindAppUidEventQueue = function(app, event, callback){
var self = this;
this.$.getChannel(function(err, chan, done) {
if (err) {
return callback(err);
}
var name = self.name+'.event.' + self.uid + '.' + app;
chan.queue.declare(name, {durable: true, exclusive: true}, function (err) {
if (err) {
done(err);
return callback(err);
}
chan.queue.bind(name, 'event.bus', app+'.'+event, {}, function (err) {
done(err);
return callback(err);
});
});
});
};
RabbitHeraldClient.prototype._declareAndBindAppEventQueue = function(app, event, callback){
var self = this;
this.$.getChannel(function(err, chan, done) {
if (err) {
return callback(err);
}
var name = self.name+'.event.' + app;
chan.queue.declare(name, {durable: true}, function (err) {
if (err) {
done(err);
return callback(err);
}
chan.queue.bind(name, 'event.bus', app+'.'+event, {}, function (err) {
done(err);
return callback(err);
});
});
});
};
RabbitHeraldClient.prototype._bindQueueBasic = function(callback){
var self = this;
this.$.getChannel(function(err, chan, done) {
if (err) {
return callback(err);
}
chan.queue.bind(self.name+'.rpc.in', 'rpc.bus', self.name, {}, function(err){
if (err) {
done(err);
return callback(err);
}
chan.queue.bind(self.name+'.rpc.in.'+self.uid, 'rpc.bus', self.name+'-all', {}, function(err){
if (err) {
done(err);
return callback(err);
}
chan.queue.bind(self.name+'.rpc.in.'+self.uid, 'rpc.bus', self.name+'.'+self.uid, {}, function(err) {
if (err) {
done(err);
return callback(err);
}
chan.queue.bind(self.name+'.rpc.res.'+self.uid, 'rpc.res.bus', self.name+'.rpc.res.'+self.uid, {}, function(err) {
if (err) {
done(err);
return callback(err);
}
chan.queue.bind(self.name + '.rpc.res', 'rpc.res.bus', self.name + '.rpc.res', {}, function (err) {
done(err);
return callback(err);
});
});
});
});
});
});
};
RabbitHeraldClient.prototype._consumeRpcIn = function(){
var self = this;
self.$.getChannel(function(err, chan, done){
if (err) throw err;
self._declareAndBind(function(){
chan.basic.qos({prefetchCount: 100}, function () {
chan.basic.consume(self.name + '.rpc.in', {}, self._parseRpcMessage.bind(self, chan), function (err) {
if (err) console.error(err);
});
});
});
chan.channel.once('close', function(err){
if (err){
self.emit('error', err);
setTimeout(function(){
self._consumeRpcIn();
}, 1000);
}
});
});
};
RabbitHeraldClient.prototype._consumeRpcResIn = function(){
var self = this;
self.$.getChannel(function(err, chan, done){
if (err) throw err;
self._declareAndBind(function() {
chan.basic.qos({prefetchCount: 100}, function () {
chan.basic.consume(self.name + '.rpc.res', {}, self._parseRpcResMessage.bind(self, chan), function (err) {
if (err) console.error(err);
});
});
});
chan.channel.once('close', function(err){
if (err){
self.emit('error', err);
setTimeout(function() {
self._consumeRpcResIn();
}, 1000);
}
})
});
};
RabbitHeraldClient.prototype._consumeRpcInUid = function(){
var self = this;
self.$.getChannel(function(err, chan, done){
if (err) throw err;
self._declareAndBind(function() {
chan.basic.qos({prefetchCount: 100}, function () {
chan.basic.consume(self.name + '.rpc.in.' + self.uid, {}, self._parseRpcMessage.bind(self, chan), function (err) {
if (err) console.error(err);
});
});
});
chan.channel.once('close', function(err){
if (err){
self.emit('error', err);
setTimeout(function() {
self._consumeRpcInUid();
}, 1000);
}
})
});
};
RabbitHeraldClient.prototype._consumeRpcResInUid = function(){
var self = this;
self.$.getChannel(function(err, chan, done){
if (err) throw err;
self._declareAndBind(function() {
chan.basic.qos({prefetchCount: 100}, function () {
chan.basic.consume(self.name + '.rpc.res.' + self.uid, {}, self._parseRpcResMessage.bind(self, chan), function (err) {
if (err) console.error(err);
});
});
});
chan.channel.once('close', function(err){
if (err){
self.emit('error', err);
setTimeout(function() {
self._consumeRpcResInUid();
}, 1000);
}
})
});
};
RabbitHeraldClient.prototype._initRoutes = function(callback){
var self = this;
self._declareExchange(function(err){
self._declareAppQueue(function(err){
self._declareAppUidQueue(function(err){
self._bindQueueBasic(function(err){
setImmediate(function(){
self._consumeRpcIn();
});
setImmediate(function(){
self._consumeRpcIn();
});
setImmediate(function(){
self._consumeRpcResIn();
});
setImmediate(function(){
self._consumeRpcResInUid();
});
callback(null);
});
});
});
});
};
RabbitHeraldClient.prototype._declareAndBind = function(cb){
var self = this;
self._declareExchange(function(err){
self._declareAppQueue(function(err){
self._declareAppUidQueue(function(err){
self._bindQueueBasic(function(err){
cb(null);
});
});
});
});
};
/**
* Connect to server. Подключает к серверу.
* @param {Object} [settings] Settings for connect. Default get from RabbitHeraldClient object.
* Настройки для подключения. По умолчанию берет растройки из самого объекта.
*/
RabbitHeraldClient.prototype.connect = function(settings){
settings = settings || {};
if (this.connected) return;
if (this.connecting) return;
this.connecting = true;
this.should_work = true;
this.connectProp = settings.connect || this.connectProp || {};
this.connectProp.host = this.connectProp.host || '127.0.0.1';
this.connectProp.port = this.connectProp.port || 5672;
this.connectProp.user = this.connectProp.user || 'guest';
this.connectProp.password = this.connectProp.password || 'guest';
this.connectProp.vhost = this.connectProp.vhost || '/';
if (this.$) this.$.disconnect(function(){});
var self = this;
this.$ = new (require('bramqp-wrapper'))(this.connectProp);
this.$.connect(function(err){
if (err){
self.connecting = false;
return self.emit('error', err);
}
});
self.$.once('connect', function(){
self.$.on('error', self.emit.bind(self, 'error'));
self.$.on('disconnect', self.emit.bind(self, 'close', 'RABBIT_DISCONNECT'));
});
self.$.on('connect', function(){
self.emit('connect');
self._initRoutes(function(err){
if (err) self.emit('error', err);
});
self.connected = true;
self.connecting = false;
});
};
/**
* Close connect. Закрывает подключение.
* @param {Function} [callback]
*/
RabbitHeraldClient.prototype.close = function(callback){
this.should_work = false;
this.$.disconnect(callback || function(){});
};
/**
* Close connect. Закрывает подключение.
* @param {Function} [callback]
*/
RabbitHeraldClient.prototype.end = function(callback){
this.should_work = false;
this.$.disconnect(callback || function(){});
};
/**
* Unref client.
* @deprecated
* @returns {*}
*/
RabbitHeraldClient.prototype.unref = require('util').deprecate(function(){});
/**
* Ref client.
* @deprecated
* @returns {*}
*/
RabbitHeraldClient.prototype.ref = require('util').deprecate(function(){});
/**
* One more constructor.
* Еще один конструктор.
* @param {Object} [settings] Settings for RabbitHeraldClient.
* Настройки для RabbitHeraldClient.
* @param {Object} [settings.default] Default settings for messaging.
* Настройки для сообщений по умолчанию.
* @param {Boolean} [settings.forceName=false] If `true` - `settings.name` can change name to some value, else use `settings.connect.user`.
* Если `true` - `settings.name` может выставить значение name, иначе, используется `settings.connect.user`.
* @param {String} [settings.name=(settings.forceName?Math.random():settings.connect.user)] Name of application or client.
* Название клиента или приложения.
* @param {String} [settings.uid=this.name+Math.random()] Unique id of application or client.
* Уникальный id приложения или клиента.
* @param {Logger|Object} [settings.logger] Object for log events.
* Объект для логгирования событий.
* @param {Object} [settings.connect='{port:5672, user:'guest', password:'guest'}'] Object of connection properties.
* Объект настроек подключения к серверу.
* @param {CryptMaker|Object} [settings.messageMaker] Object with make and parse logic.
* Объект, реализующий логику создания сообщений и их парсинга.
* @param {String} [needCrypt='no'] If need crypt messages - algorithm for crypt. By default doesn't encrypt.
* Если необходимо шифровать сообщения - алогоритм шифрования. По умолчанию не шифрует.
* @param {String} [key] Encryption key.
* Ключ шифрования.
* @returns {RabbitHeraldClient}
*/
RabbitHeraldClient.createClient = function(settings, needCrypt, key){
var rhc = new RabbitHeraldClient(settings, needCrypt, key);
rhc.connect();
return rhc;
};
module.exports = RabbitHeraldClient;