@pandorajs/hub
Version:
pandora.js messenge hub
242 lines • 8.43 kB
JavaScript
"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