UNPKG

@pandorajs/hub

Version:

pandora.js messenge hub

242 lines 8.43 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.HubServer = void 0; const messenger_1 = require("@pandorajs/messenger"); const const_1 = require("../const"); const RouteTable_1 = require("./RouteTable"); const Balancer_1 = require("./Balancer"); const util_1 = require("util"); const events_1 = require("events"); /** * IPC-Hub */ class HubServer extends events_1.EventEmitter { constructor() { super(...arguments); this.routeTable = new RouteTable_1.RouteTable(); /** * Count of pending reply transactions * @type {number} */ this.pendingReplyCount = 0; } /** * Start Hub * @return {Promise<void>} */ async start() { if (this.messengerServer) { throw new Error('Hub already started'); } this.messengerServer = new messenger_1.MessengerServer({ name: const_1.HUB_SOCKET_NAME, responseTimeout: const_1.TIMEOUT_OF_RESPONSE, }); this.startListen(); await new Promise(resolve => { this.messengerServer.ready(resolve); }); } /** * Handle message from clients * @param {MessagePackage} message * @param {ForceReplyFn} reply */ handleMessageIn(message, reply) { if (message.needReply) { this.pendingReplyCount++; } const originReply = reply; reply = replyData => { this.pendingReplyCount--; originReply(replyData); }; // Broadcast to all clients if message.broadcast be true and no message.remote // Only broadcast, working in very low level if (message.broadcast && !message.remote) { try { this.messengerServer.broadcast(const_1.PANDORA_HUB_ACTION_MSG_DOWN, message); if (message.needReply) { reply({ success: true }); } } catch (error) { if (message.needReply) { reply({ success: false, error }); } } return; } try { const clients = this.routeTable.selectClients(message.remote); if (!clients.length) { throw new Error(util_1.format('Cannot found any clients by selector: %j', message.remote)); } if (message.broadcast) { this.broadcastToClients(clients, message, reply); } else { this.balanceToClients(clients, message, reply); } } catch (error) { if (message.needReply) { reply({ success: false, error }); } } } /** * Handle kind of message as {remote: {}, broadcast: false} * @param clients * @param message * @param reply */ balanceToClients(clients, message, reply) { const balancer = new Balancer_1.Balancer(clients); const { client, selector: hitSelector } = balancer.pick(); const callback = message.needReply ? (error, res) => { if (error) { reply({ host: hitSelector, success: false, error: error, }); } else { reply(res); } } : null; // Dispatch the message to a random client of all selected clients client.send(const_1.PANDORA_HUB_ACTION_MSG_DOWN, message, callback, message && message.timeout); } /** * Handle kind of message as {remote: {}, broadcast: true} * @param clients * @param message * @param reply */ broadcastToClients(clients, message, reply) { const expectFoundNumber = clients.length; const batchReply = []; for (const { selector: hitSelector, client } of clients) { const callback = message.needReply ? (error, res) => { if (error) { batchReply.push({ host: hitSelector, success: false, error: error, }); } else { batchReply.push(res); } if (batchReply.length === expectFoundNumber) { reply({ success: true, remote: message.host, batchReply, }); } } : null; // Dispatch the message to all selected clients client.send(const_1.PANDORA_HUB_ACTION_MSG_DOWN, message, callback, message && message.timeout); } } /** * Start listen on this.messengerServer */ startListen() { this.messengerServer.on('connected', (client) => { // this.messengerServer will ignore error this.routeTable.setRelation(client, { initialization: true }); }); this.messengerServer.on('disconnected', (client) => { // this.messengerServer will ignore error const selectors = this.routeTable.getSelectorsByClient(client); this.routeTable.forgetClient(client); if (selectors) { this.emit('client_disconnected', selectors); } }); this.messengerServer.on(const_1.PANDORA_HUB_ACTION_ONLINE_UP, (message, reply, client) => { try { this.routeTable.setRelation(client, message.host); reply({ success: true }); } catch (error) { reply({ success: false, error }); } }); this.messengerServer.on(const_1.PANDORA_HUB_ACTION_OFFLINE_UP, (message, reply, client) => { try { const selectors = this.routeTable.getSelectorsByClient(client); this.routeTable.forgetClient(client); if (selectors) { this.emit('client_disconnected', selectors); } reply({ success: true }); } catch (error) { reply({ success: false, error }); } }); this.messengerServer.on(const_1.PANDORA_HUB_ACTION_PUBLISH_UP, (message, reply, client) => { try { this.routeTable.setRelation(client, message.data.selector); reply({ success: true }); } catch (error) { reply({ success: false, error }); } }); this.messengerServer.on(const_1.PANDORA_HUB_ACTION_UNPUBLISH_UP, (message, reply, client) => { try { this.routeTable.forgetRelation(client, message.data.selector); reply({ success: true }); } catch (error) { reply({ success: false, error }); } }); this.messengerServer.on(const_1.PANDORA_HUB_ACTION_MSG_UP, this.handleMessageIn.bind(this)); } /** * Stop listen on this.messengerServer */ stopListen() { this.messengerServer.removeAllListeners('connected'); this.messengerServer.removeAllListeners('disconnected'); this.messengerServer.removeAllListeners(const_1.PANDORA_HUB_ACTION_ONLINE_UP); this.messengerServer.removeAllListeners(const_1.PANDORA_HUB_ACTION_OFFLINE_UP); this.messengerServer.removeAllListeners(const_1.PANDORA_HUB_ACTION_MSG_UP); } /** * Stop Hub * @return {Promise<void>} */ async stop() { if (!this.messengerServer) { throw new Error('Hub has not started yet'); } await new Promise((resolve, reject) => { this.stopListen(); this.messengerServer.close(err => { this.messengerServer = null; if (err) { reject(err); return; } resolve(); }); }); } getMessengerServer() { return this.messengerServer; } } exports.HubServer = HubServer; //# sourceMappingURL=HubServer.js.map