From 6a9c6103796d697719e6e5e27d4801dec9235f9d Mon Sep 17 00:00:00 2001 From: prlanzarin Date: Fri, 26 Jan 2018 19:10:01 +0000 Subject: [PATCH] Refactored subprocesses handling in bbb-webrtc-sfu They should now restart when crashing and shouldnt be stuck anymore --- labs/bbb-webrtc-sfu/lib/ProcessManager.js | 116 ++++++++++++++++++ .../lib/screenshare/ScreenshareProcess.js | 9 +- labs/bbb-webrtc-sfu/lib/video/VideoManager.js | 3 - labs/bbb-webrtc-sfu/lib/video/VideoProcess.js | 9 +- labs/bbb-webrtc-sfu/server.js | 57 +-------- 5 files changed, 137 insertions(+), 57 deletions(-) create mode 100644 labs/bbb-webrtc-sfu/lib/ProcessManager.js diff --git a/labs/bbb-webrtc-sfu/lib/ProcessManager.js b/labs/bbb-webrtc-sfu/lib/ProcessManager.js new file mode 100644 index 0000000000..1c28171023 --- /dev/null +++ b/labs/bbb-webrtc-sfu/lib/ProcessManager.js @@ -0,0 +1,116 @@ +/* + * Lucas Fialho Zawacki + * Paulo Renato Lanzarin + * (C) Copyright 2017 Bigbluebutton + * + */ + +'use strict'; + +const cp = require('child_process'); +const Logger = require('./utils/Logger'); +const SCREENSHARE_PATH = './lib/screenshare/ScreenshareProcess'; +const VIDEO_PATH = './lib/video/VideoProcess.js'; + +module.exports = class ProcessManager { + constructor() { + this.screensharePid; + this.videoPid; + this.screenshareProcess; + this.videoProcess; + this.processes = {}; + this.runningState = "RUNNING"; + } + + async start () { + let screenshareProcess = this.startProcess(SCREENSHARE_PATH); + + let videoProcess = this.startProcess(VIDEO_PATH); + + this.processes[screenshareProcess.pid] = screenshareProcess; + this.processes[videoProcess.pid] = videoProcess; + + process.on('SIGTERM', async () => { + await this.finishChildProcesses(); + process.exit(0); + }); + + process.on('SIGINT', async () => { + await this.finishChildProcesses(); + process.exit(0); + }); + + process.on('uncaughtException', async (error) => { + Logger.error('[ProcessManager] Uncaught exception', error.stack); + await this.finishChildProcesses(); + process.exit('1'); + }); + + // Added this listener to identify unhandled promises, but we should start making + // sense of those as we find them + process.on('unhandledRejection', (reason, p) => { + Logger.error('[ProcessManager] Unhandled Rejection at: Promise', p, 'reason:', reason); + }); + } + + startProcess (processPath) { + Logger.info("[ProcessManager] Starting process at path", processPath); + let proc = cp.fork(processPath, { + // Pass over all of the environment. + env: process.ENV, + // Share stdout/stderr, so we can hear the inevitable errors. + silent: false + }); + + proc.path = processPath; + + proc.on('message', this.onMessage); + proc.on('error', this.onError); + + proc.on('disconnect', (error) => { + Logger.info('[ProcessManager] Received disconnect event from child process with PID', this.pid, ', killing it'); + let processId = proc.pid; + + proc.kill(); + if (this.runningState === 'RUNNING') { + Logger.info('[ProcessManager] Restarting process', processId, 'because server is still running...'); + this.restartProcess(processId); + } + }); + + return proc; + } + + restartProcess (pid) { + let process = this.processes[pid]; + if (typeof process !== 'undefined' && process) { + let newProcess = this.startProcess(process.path); + this.processes[newProcess.pid] = newProcess; + delete this.processes[pid]; + } + } + + onMessage (message) { + Logger.info('[ProcessManager] Received child message from', this.pid, message); + } + + onError (e) { + Logger.error('[ProcessManager] Received child error', this.pid, e); + } + + onDisconnect (e) { + } + + async finishChildProcesses () { + this.runningState = "STOPPING"; + + for (var process in this.processes) { + let procObj = this.processes[process]; + if (procObj.kill === 'function' && !procObj.killed) { + await procObj.disconnect() + } + } + + this.runningState = "STOPPED"; + } +} diff --git a/labs/bbb-webrtc-sfu/lib/screenshare/ScreenshareProcess.js b/labs/bbb-webrtc-sfu/lib/screenshare/ScreenshareProcess.js index a53152f4f7..cf5929aef1 100644 --- a/labs/bbb-webrtc-sfu/lib/screenshare/ScreenshareProcess.js +++ b/labs/bbb-webrtc-sfu/lib/screenshare/ScreenshareProcess.js @@ -1,7 +1,10 @@ +'use strict'; + const ScreenshareManager = require('./ScreenshareManager'); const Logger = require('../utils/Logger'); const config = require('config'); + if (config.get('acceptSelfSignedCertificate')) { process.env.NODE_TLS_REJECT_UNAUTHORIZED=0; } @@ -12,7 +15,11 @@ process.on('uncaughtException', (error) => { Logger.error('[ScreenshareProcess] Uncaught exception ', error.stack); }); -process.on('disconnect', c.stopAll); +process.on('disconnect', async () => { + Logger.warn('[ScreenshareProcess] Parent process exited, cleaning things up and finishing child now...'); + await c.stopAll(); + process.exit(0); +}); // Added this listener to identify unhandled promises, but we should start making // sense of those as we find them diff --git a/labs/bbb-webrtc-sfu/lib/video/VideoManager.js b/labs/bbb-webrtc-sfu/lib/video/VideoManager.js index d33c69afa3..d3c3481b04 100755 --- a/labs/bbb-webrtc-sfu/lib/video/VideoManager.js +++ b/labs/bbb-webrtc-sfu/lib/video/VideoManager.js @@ -221,6 +221,3 @@ let logAvailableSessions = function() { } } } - -process.on('SIGTERM', stopAll); -process.on('SIGINT', stopAll); diff --git a/labs/bbb-webrtc-sfu/lib/video/VideoProcess.js b/labs/bbb-webrtc-sfu/lib/video/VideoProcess.js index 1e06fea875..116bcf16ab 100644 --- a/labs/bbb-webrtc-sfu/lib/video/VideoProcess.js +++ b/labs/bbb-webrtc-sfu/lib/video/VideoProcess.js @@ -1,3 +1,5 @@ +'use strict'; + const Logger = require('../utils/Logger'); const config = require('config'); @@ -13,8 +15,11 @@ process.on('uncaughtException', (error) => { Logger.error('[VideoProcess] Uncaught exception ', error.stack); }); -process.on('disconnect', () => { - Logger.info("[VideoProcess] Parent process disconnected!"); +process.on('disconnect', async () => { + Logger.warn('[VideoProcess] Parent process exited, cleaning things up and finishing child now...'); + //TODO below + //async VideoManager.stopAll(); + process.exit(0); }); // Added this listener to identify unhandled promises, but we should start making diff --git a/labs/bbb-webrtc-sfu/server.js b/labs/bbb-webrtc-sfu/server.js index 27c1b10e1c..0e04fad4c4 100755 --- a/labs/bbb-webrtc-sfu/server.js +++ b/labs/bbb-webrtc-sfu/server.js @@ -5,64 +5,19 @@ * */ +'use strict'; + const ConnectionManager = require('./lib/connection-manager/ConnectionManager'); const HttpServer = require('./lib/connection-manager/HttpServer'); const server = new HttpServer(); const WebsocketConnectionManager = require('./lib/connection-manager/WebsocketConnectionManager'); -const cp = require('child_process'); const Logger = require('./lib/utils/Logger'); +const ProcessManager = require('./lib/ProcessManager.js'); +const PM = new ProcessManager(); -let screenshareProc = cp.fork('./lib/screenshare/ScreenshareProcess', { - // Pass over all of the environment. - env: process.ENV, - // Share stdout/stderr, so we can hear the inevitable errors. - silent: false -}); +PM.start(); -let videoProc = cp.fork('./lib/video/VideoProcess.js', { - // Pass over all of the environment. - env: process.ENV, - // Share stdout/stderr, so we can hear the inevitable errors. - silent: false -}); - -let onMessage = function (message) { - Logger.info('event','child message',this.pid,message); -}; - -let onError = function(e) { - Logger.error('event','child error',this.pid,e); -}; - -let onDisconnect = function(e) { - Logger.info(e); - Logger.info('event','child disconnect',this.pid,'killing...'); - this.kill(); -}; - -screenshareProc.on('message',onMessage); -screenshareProc.on('error',onError); -screenshareProc.on('disconnect',onDisconnect); - -videoProc.on('message',onMessage); -videoProc.on('error',onError); -videoProc.on('disconnect',onDisconnect); - -process.on('SIGTERM', process.exit) -process.on('SIGINT', process.exit) - -process.on('uncaughtException', (error) => { - Logger.error('[MainProcess] Uncaught exception', error.stack); - process.exit('1'); -}); - -// Added this listener to identify unhandled promises, but we should start making -// sense of those as we find them -process.on('unhandledRejection', (reason, p) => { - Logger.error('[MainProcess] Unhandled Rejection at: Promise', p, 'reason:', reason); -}); - -const CM = new ConnectionManager(screenshareProc, videoProc); +const CM = new ConnectionManager(PM.screenshareProcess, PM.videoProcess); let websocketManager = new WebsocketConnectionManager(server.getServerObject(), '/bbb-webrtc-sfu');