dl
Version:
DreamLab Libs
244 lines (191 loc) • 9.34 kB
JavaScript
var core = require('core');
var RbmqExchange = core.client.rabbitmq.RbmqExchange;
var RbmqIncomingMessage = core.client.rabbitmq.RbmqIncomingMessage;
var RbmqOutcomingMessage = core.client.rabbitmq.RbmqOutcomingMessage;
// *************************************************
// QueueIncomingMessageProcessorFactory
// *************************************************
exports.QueueIncomingMessageProcessorFactory = {
get: function(queueConfig, rbmqConnection, queueLogger) {
var postponeConfig = queueConfig.postpone || {};
var postponeMethod = postponeConfig.method;
var processorClass;
if (!postponeMethod) {
processorClass = QueueIncomingMessageProcessor;
} else if (postponeMethod == QueueIncomingMessageProcessor.POSTPONE_METHOD.DLE) {
processorClass = DLEPostponedMessageProcessor;
} else if (postponeMethod == QueueIncomingMessageProcessor.POSTPONE_METHOD.ACK) {
processorClass = AckPublishPostponedMessageProcessor;
} else if (postponeMethod == QueueIncomingMessageProcessor.POSTPONE_METHOD.REJECT) {
processorClass = RejectPublishPostponedMessageProcessor;
} else {
throw new Error('Postpone method', postponeMethod, 'not implemented (yet?)');
}
return new processorClass(postponeConfig, rbmqConnection, queueLogger);
}
};
// *************************************************
// QueueIncomingMessageProcessor
// *************************************************
var QueueIncomingMessageProcessor = function(postponeConfig, rbmqConnection) {
this._rbmqConnection = rbmqConnection;
this._postponeConfig = postponeConfig;
this._postponerEnabled = false;
this._skipMessages = postponeConfig.skip || [];
};
QueueIncomingMessageProcessor.prototype.isPostponerEnabled = function() {
return this._postponerEnabled;
};
QueueIncomingMessageProcessor.prototype.acknowledge = function(msg) {
try {
RbmqIncomingMessage.prototype.acknowledge.call(msg);
} catch (err) {
console.error('Error while trying to acknowledge message:', err);
}
};
QueueIncomingMessageProcessor.prototype.reject = function(msg, requeue) {
try {
RbmqIncomingMessage.prototype.reject.call(msg, requeue);
} catch (err) {
console.error('Error while trying to reject message:', err);
}
};
QueueIncomingMessageProcessor.prototype._preProcessMessage = function(msg) {
if (this._skipMessages.indexOf(msg.getMessageId()) != -1) {
console.warn('IncomingMessageProcessor: skipping postpone message:', msg.getMessageId());
this.reject(msg, false);
return false;
}
if (msg._completed) {
throw new Error('Message already acknowledged or rejected');
}
return true;
};
QueueIncomingMessageProcessor.prototype.postpone = function(msg) {
if (!this._preProcessMessage(msg)) {
return;
}
console.error('Postpone is not enabled for this queue. Reject && requeue');
this.reject(msg, true);
};
QueueIncomingMessageProcessor.POSTPONE_METHOD = {};
QueueIncomingMessageProcessor.POSTPONE_METHOD.DLE = 'dle';
QueueIncomingMessageProcessor.POSTPONE_METHOD.ACK = 'ack';
QueueIncomingMessageProcessor.POSTPONE_METHOD.REJECT = 'reject';
QueueIncomingMessageProcessor.POSTPONE_STRATEGY = {};
QueueIncomingMessageProcessor.POSTPONE_STRATEGY.LINEAR = 'linear';
QueueIncomingMessageProcessor.POSTPONE_STRATEGY.EXPONENTIAL = 'exponential';
QueueIncomingMessageProcessor.POSTPONE_STRATEGY.CONSTANT = 'constant';
QueueIncomingMessageProcessor.MAX_POSTPONE_TIME = 1000 * 60 * 60 * 24 * 7; // 7 days
exports.QueueIncomingMessageProcessor = QueueIncomingMessageProcessor;
// *************************************************
// DLEPostponedMessageProcessor
// *************************************************
var DLEPostponedMessageProcessor = function() {
QueueIncomingMessageProcessor.apply(this, arguments);
this._postponerEnabled = true;
};
DLEPostponedMessageProcessor.prototype = Object.create(QueueIncomingMessageProcessor.prototype);
DLEPostponedMessageProcessor.prototype.reject = function(msg, requeue) {
if (!requeue) {
QueueIncomingMessageProcessor.prototype.acknowledge.call(this, msg);
} else {
QueueIncomingMessageProcessor.prototype.reject.call(this, msg, true);
}
};
DLEPostponedMessageProcessor.prototype.postpone = function(msg) {
if (!this._preProcessMessage(msg)) {
return;
}
QueueIncomingMessageProcessor.prototype.reject.call(this, msg, false);
};
// *************************************************
// PublishPostponedMessageProcessor
// *************************************************
var PublishPostponedMessageProcessor = function(postponeConfig, rbmqConnection, queueLogger) {
QueueIncomingMessageProcessor.call(this, postponeConfig, rbmqConnection);
this._postponerEnabled = true;
this._logger = queueLogger;
this._postponeExchange = new RbmqExchange(this._rbmqConnection, postponeConfig.exchange);
this._postponeExchange.open();
};
PublishPostponedMessageProcessor.prototype = Object.create(QueueIncomingMessageProcessor.prototype);
PublishPostponedMessageProcessor.prototype._getExpirationForStrategy = function(msg) {
var config = this._postponeConfig;
var expiration;
switch (config.strategy) {
case QueueIncomingMessageProcessor.POSTPONE_STRATEGY.LINEAR:
expiration = Math.round((msg.getPostponeCount() + 1) * config.factor);
break;
case QueueIncomingMessageProcessor.POSTPONE_STRATEGY.EXPONENTIAL:
expiration = Math.round(1000 * Math.pow(config.factor, msg.getPostponeCount()));
break;
case QueueIncomingMessageProcessor.POSTPONE_STRATEGY.CONSTANT:
expiration = Math.round(config.factor);
break;
}
return Math.min(expiration, QueueIncomingMessageProcessor.MAX_POSTPONE_TIME);
};
PublishPostponedMessageProcessor.prototype.postpone = function(msg, expiration) {
if (!this._preProcessMessage(msg)) {
return;
}
var config = this._postponeConfig;
if (config.exchange.publish.messageSizeLimit && msg.getSize() > config.exchange.publish.messageSizeLimit) {
console.error('Message too big. Current limit is ' + config.exchange.publish.messageSizeLimit + '. Reject && requeue');
QueueIncomingMessageProcessor.prototype.reject.call(this, msg, true);
return;
}
if (!this._postponeExchange.isOpened()) {
console.warn('Postpone channel not opened. Reject && requeue');
QueueIncomingMessageProcessor.prototype.reject.call(this, msg, true);
return;
}
var postponeMsg = new RbmqOutcomingMessage(msg._body, msg._data);
postponeMsg.setHeader('x-postpone-count', (msg.getPostponeCount() + 1).toString());
postponeMsg.setHeader('x-postpone-routing-key', msg.getRoutingKey());
postponeMsg.setHeader('x-msg-created', msg.getCreateTimestamp().toString());
postponeMsg.setHeader('x-msg-modified', Date.now().toString());
postponeMsg.setRoutingKey(config.exchange.publish.routingKey);
if (expiration === undefined && config.strategy) {
expiration = this._getExpirationForStrategy(msg);
}
if (expiration !== undefined) {
console.log('PublishPostponedMessageProcessor/postpone set expiration = %s strategy = %s', expiration, config.strategy);
postponeMsg.setExpiration(expiration.toString());
}
var that = this;
postponeMsg.addEventListener(RbmqOutcomingMessage.Event.PUBLISHED, function(ev) {
msg._completed = false;
that._postProcessMessage(msg);
});
postponeMsg.addEventListener(RbmqOutcomingMessage.Event.REJECTED, function(ev) {
that._logger.log('subscribe', 'postpone.error', msg.getMessageId());
console.error('Message rejected while trying to postpone', msg.getMessageId(), '. Reject && requeue');
msg._completed = false;
QueueIncomingMessageProcessor.prototype.reject.call(this, msg, true);
});
// Lock message since it's published to postponer. Just to avoid ack meanwhile.
msg._completed = true;
this._postponeExchange.publish(postponeMsg);
};
// *************************************************
// AckPublishPostponedMessageProcessor
// *************************************************
var AckPublishPostponedMessageProcessor = function() {
PublishPostponedMessageProcessor.apply(this, arguments);
};
AckPublishPostponedMessageProcessor.prototype = Object.create(PublishPostponedMessageProcessor.prototype);
AckPublishPostponedMessageProcessor.prototype._postProcessMessage = function(msg) {
QueueIncomingMessageProcessor.prototype.acknowledge.call(this, msg);
};
// *************************************************
// RejectPublishPostponedMessageProcessor
// *************************************************
var RejectPublishPostponedMessageProcessor = function() {
PublishPostponedMessageProcessor.apply(this, arguments);
};
RejectPublishPostponedMessageProcessor.prototype = Object.create(PublishPostponedMessageProcessor.prototype);
RejectPublishPostponedMessageProcessor.prototype._postProcessMessage = function(msg) {
QueueIncomingMessageProcessor.prototype.reject.call(this, msg, false);
};