UNPKG

dl

Version:

DreamLab Libs

244 lines (191 loc) 9.34 kB
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); };