UNPKG

tyo-mq

Version:

Distributed Message Pub/Sub Service with socket.io

209 lines (170 loc) 6.69 kB
/** * @file producer.js */ const util = require('util'), events = require('./events'), Constants = require('./constants'); var Subscriber = require('./subscriber'); function SubscriberInfo (name) { this.name = name; } function Publisher (name, event, options) { if (event && typeof event === 'object' && !Array.isArray(event)) { options = event; event = null; } options = options || {}; // call parent constructor Subscriber.call(this, name); var producer = this; this.eventDefault = event || Constants.EVENT_DEFAULT; if (options.default_ttl !== undefined) this.default_ttl = options.default_ttl; else if (options.defaultTtl !== undefined) this.default_ttl = options.defaultTtl; else if (options.ttl !== undefined) this.default_ttl = options.ttl; else this.default_ttl = null; this.subscribers = {}; this.onSubscriptionListener = null; /** * @override * */ this.sendIdentificationInfo = function () { var payload = {name: name}; if (producer.default_ttl !== null && producer.default_ttl !== undefined) payload.default_ttl = producer.default_ttl; this.sendMessage.call(producer, 'PRODUCER', payload); }; /** * Chunk size threshold: messages serialised above this byte length are split * into multiple PRODUCE_CHUNK frames instead of one PRODUCE frame, keeping * each frame safely under the engine.io maxHttpBufferSize limit. */ Publisher.CHUNK_SIZE = Publisher.CHUNK_SIZE || 256 * 1024; // 256 KB /** * Event produce function * @param event * @param data * @param validFor, the valid time (lifespan of the message) */ this.produce = function (event, data, validFor, to) { var self = this; if (data === undefined) { data = event; event = this.eventDefault; if (!event) throw new Error('Default event name is not set.'); } var options = {}; if (validFor && typeof validFor === 'object' && !Array.isArray(validFor)) options = validFor; var message = {event:event, message:data, from:self.name}; if (options.guaranteed || options.guaranteed_delivery || options.guaranteedDelivery || options.delivery === 'guaranteed' || options.delivery_mode === 'guaranteed') message.guaranteed = true; if (options.ttl !== undefined) message.ttl = options.ttl; else if (self.default_ttl !== null && self.default_ttl !== undefined) message.ttl = self.default_ttl; else if (validFor !== undefined && !(validFor && typeof validFor === 'object')) message.lifespan = validFor; if (options.broadcast) { message.method = 'broadcast'; message.broadcast = options.broadcast === 'group' ? 'group' : 'realm'; if (options.group) message.group = options.group; } if (to) message.to = to; var str = JSON.stringify(message); var chunkSize = Publisher.CHUNK_SIZE; if (str.length <= chunkSize) { self.sendMessage.call(self, 'PRODUCE', message, self.name); return; } // Large message — split into chunks var total = Math.ceil(str.length / chunkSize); var transferId = Date.now().toString(36) + '-' + Math.random().toString(36).slice(2); for (var i = 0; i < total; i++) { self.sendMessage.call(self, 'PRODUCE_CHUNK', { transferId: transferId, index: i, total: total, data: str.slice(i * chunkSize, (i + 1) * chunkSize) }, self.name); } }; /** * On Subscribe */ this.setOnSubscriptionListener = function (callback) { var event = events.toOnSubscribeEvent(this.getId()); this.off(event); // clear all previous listeners this.on(event, function (data) { producer.logger.log("Received subscription information: " + JSON.stringify(data)); producer.subscribers[data.id] = data; // further listener if (producer.onSubscriptionListener) producer.onSubscriptionListener.call(producer, data); if (callback) callback(data); }); }; this.onSubscribed = this.setOnSubscriptionListener; /** * On Lost connections with subscriber(s) */ this.setOnSubscriberLostListener = function (callback) { var event = events.toOnDisconnectEvent(this.getId()); this.off(event); // clear all previous listeners this.on(event, function(data) { producer.logger.log("Lost subscriber's connection"); if (callback) callback(data); }); }; this.onSubscriberLost = this.setOnSubscriberLostListener; /** * On Unsubsribe */ this.setOnUnsubscribedListener = function (callback) { var event = events.toOnUnsubscribeEvent(this.getId()); this.on(event, function() { if (callback) callback(); }); }; this.onUnsubscribed = this.setOnUnsubscribedListener; // Initialisation this.addConnectionListener(function () { var producerName = producer.name || Constants.ANONYMOUS; // producer.sendMessage.call(producer, 'PRODUCER', {name: producerName}); producer.setOnSubscriptionListener(); }); // this.setOnSubscriberLostListener = function (callback) { // var event = events.toOnDisconnectEvent(this.getId()); // this.on(event, function(data) { // producer.logger.log("Lost subscriber's connection"); // if (callback) // callback(data); // }); // }; // this.setOnSubscriberLost = this.setOnSubscriberLostListener; this.setOnSubscriberOnlineListener = function (callback) { var event = events.toOnConnectEvent(this.getId()); this.off(event); // clear all previous listeners this.on(event, function(data) { producer.logger.log("Subscriber is online: name='" + (data && data.consumer) + "' id=" + (data && data.id) + " socket=" + (data && data.socket)); if (callback) callback(data); }); } this.whenSubscriberOnline = this.setOnSubscriberOnline = this.setOnSubscriberOnlineListener; } /** * Inherits from Socket */ util.inherits(Publisher, Subscriber); module.exports = Publisher;