UNPKG

@mediarithmics/plugins-nodejs-sdk

Version:

This is the mediarithmics nodejs to help plugin developers bootstrapping their plugin without having to deal with most of the plugin boilerplate

153 lines (125 loc) 4.82 kB
import cluster, { Worker } from 'cluster'; import { Server } from 'http'; import { cpus } from 'os'; import { BasePlugin } from './BasePlugin'; export enum MsgCmd { LOG_LEVEL_UPDATE_FROM_WORKER, LOG_LEVEL_UPDATE_FROM_MASTER, GET_LOG_LEVEL_REQUEST, } export interface SocketMsg { cmd: MsgCmd; value?: string; } export class ProductionPluginRunner { numCPUs = cpus().length; pluginPort: number; plugin: BasePlugin; server: Server; constructor(plugin: BasePlugin) { this.plugin = plugin; } updatePluginLogLevel = (value: string) => { this.plugin.logger.level = value; }; broadcastLogLevelToWorkers = () => { const msg = { cmd: MsgCmd.LOG_LEVEL_UPDATE_FROM_MASTER, value: this.plugin.logger.level, }; for (const id in cluster.workers) { const worker = cluster.workers[id]; if (worker) { worker.send(msg); } } }; /** * Socker Listener for master process. It has 4 jobs: * 1 - If a new token is detected by a Worker, it receives a message and should send a message to each workers * 2 - If a worker is asking for a token update (ex: the worker was just created because one of his friends died), the master should send a message to each workers * 3 - If a log level change is detected by a worker, it receives a message and should send a message to each workers * 4 - If a worker is asking for a log level update (ex: the worker was just created because one of his friends died), the master should send a message to each workers * @param recMsg */ masterListener = (worker: Worker, recMsg: SocketMsg) => { this.plugin.logger.debug( `Master ${process.pid} is being called with cmd: ${MsgCmd[recMsg.cmd]}, value: ${ recMsg.value ? recMsg.value : 'undefined' }`, ); if (recMsg.cmd === MsgCmd.LOG_LEVEL_UPDATE_FROM_WORKER) { if (recMsg.value) { // If we receive a Log Level, we update the token this.updatePluginLogLevel(recMsg.value); // We send the log level to each of the workers this.broadcastLogLevelToWorkers(); } else { throw new Error( 'We received a LOG_LEVEL_UPDATE_FROM_WORKER msg without logLevel in the value field of the msg.', ); } } }; /** * Socker Listener of the workers. It should listen for Token update from master and for Log Level changes * @param recMsg */ workerListener = (recMsg: SocketMsg) => { this.plugin.logger.debug( `Worker ${process.pid} is being called with cmd: ${MsgCmd[recMsg.cmd]}, value: ${ recMsg.value ? recMsg.value : 'undefined' }`, ); if (recMsg.cmd === MsgCmd.LOG_LEVEL_UPDATE_FROM_MASTER) { if (!recMsg.value) { throw new Error( 'We received a LOG_LEVEL_UPDATE_FROM_MASTER msg without logLevel in the value field of the msg.', ); } const level = recMsg.value.toLocaleLowerCase(); this.plugin.onLogLevelUpdate(level); this.plugin.logger.debug(`${process.pid}: Updated log level with: ${JSON.stringify(this.plugin.logger.level)}`); } }; /** * Multi threading launch of the App, with socket communicaton to propagate token updates * @param port */ start(port?: number, multiProcessEnabled = false) { const pluginPort = process.env.PLUGIN_PORT; this.pluginPort = pluginPort ? parseInt(pluginPort) : 8080; const serverPort = port ? port : this.pluginPort; if (multiProcessEnabled) { if (cluster.isMaster) { this.plugin.logger.info(`Master ${process.pid} is running`); // Fork workers. for (let i = 0; i < this.numCPUs; i++) { cluster.fork(); } // Listener for when the Cluster is being called by a worker cluster.on('message', this.masterListener); // Sometimes, workers dies cluster.on('exit', (worker, code, signal) => { this.plugin.logger.info(`worker ${worker.process.pid as number} died`); // We add a new worker, with the proper socket listener cluster.fork(); }); } else { // We pass the Plugin into MT mode this.plugin.multiThread = true; // We attach a socket listener to get messages from master process.on('message', this.workerListener); const serverPort = port ? port : this.pluginPort; this.server = this.plugin.app.listen(serverPort, () => this.plugin.logger.info(`${process.pid} Plugin started, listening at ${serverPort}`), ); this.plugin.logger.info(`Worker ${process.pid} started`); } } else { this.server = this.plugin.app.listen(serverPort, () => this.plugin.logger.info(`${process.pid} Plugin started, listening at ${serverPort}`), ); } } }