From b0d72f595ebd3b6f6893ef0c38f1f312623ec625 Mon Sep 17 00:00:00 2001 From: Tjatse Date: Thu, 8 Dec 2016 17:06:18 +0800 Subject: [PATCH] refactor: performance --- pm2-gui.ini | 18 ++++-- pm2-gui.js | 170 ++++++++++++++++++++++++++++++++++------------------ 2 files changed, 124 insertions(+), 64 deletions(-) diff --git a/pm2-gui.ini b/pm2-gui.ini index f86b020..7e4b6bd 100644 --- a/pm2-gui.ini +++ b/pm2-gui.ini @@ -3,21 +3,27 @@ ; pm2 = ~/.pm2 ; -; The interval between communications of Monitor. +; The monitor will fetch system CPU/Memory amount after this certain time interval. +; it could be milliseconds or various time formats(https://github.com/zeit/ms) ; -refresh = 5000 +refresh = 5s ; ; Port of Web server and socket agent. ; port = 8088 ; -; A value indicates whether or not run the pm2-gui damonized. +; A value indicates whether or not run the pm2-gui as damonization. ; -daemonize = true +daemonize = false ; ; A value indicates whether or not the action buttons (i.e. `restart`, `stop all`...) should be displayed on web page. ; readonly = false +; +; The monitor will fetch process CPU/Memory amount after this certain time interval. +; it could be milliseconds or various time formats(https://github.com/zeit/ms) +; +process_refresh = 3s [log] ; @@ -35,7 +41,7 @@ date = false ; ; Log level, one of debug, log, info, warn, error. ; -level = warn +level = info ; ; Socket.io origins check, e.g.: ; origins = 'example.com:* http://example.com:* http://www.example.com:8088' @@ -46,7 +52,7 @@ level = warn ; ; This authorization will be used to authorize socket / web connections if it's set. ; -authorization = AuTh +;authorization = AuTh ; ; A value indicates whether agent offline or not. ; diff --git a/pm2-gui.js b/pm2-gui.js index 2102742..fd34ec8 100644 --- a/pm2-gui.js +++ b/pm2-gui.js @@ -1,23 +1,31 @@ +'use strict' + var chalk = require('chalk') var path = require('path') var fs = require('fs') var _ = require('lodash') var socketIO = require('socket.io') var inquirer = require('inquirer') + var Monitor = require('./lib/monitor') var Log = require('./lib/util/log') var Web = require('./web/index') -var layout = require('./lib/blessed-widget/layout') +var Layout = require('./lib/blessed-widget/layout') +var regLocal = /^(127\.0\.0\.1|0\.0\.0\.0|localhost)$/i + +// cli if (path.basename(process.mainModule.filename, '.js') === 'pm2-gui') { - var cmd, file - if (process.argv.length > 2) { + var cmd = 'start' + var file + var processArgvLen = process.argv.length + + if (processArgvLen > 2) { cmd = process.argv[2] } - if (process.argv.length > 3) { + if (processArgvLen > 3) { file = process.argv[3] } - cmd = cmd || 'start' switch (cmd) { case 'start': @@ -44,13 +52,17 @@ exports.startAgent = startAgent exports.dashboard = dashboard exports.exitGraceful = exitGraceful +/** + * Strat the web server by a configured file. + * @param {String} confFile full path of config file + * @return {N/A} + */ function startWebServer (confFile) { var monitor = slave({ confFile: confFile }) var options = monitor.options - - options.port = options.port || 8088 + // express server var server = Web({ middleware: function (req, res, next) { req._config = options @@ -58,7 +70,7 @@ function startWebServer (confFile) { }, port: options.port }) - + // socket.io server monitor.sockio = socketIO(server, { origins: options.origins || '*:*' }) @@ -66,18 +78,22 @@ function startWebServer (confFile) { console.info('Web server is listening on 127.0.0.1:' + options.port) } +/** + * Simply start the agent + * @param {String} confFile full path of config file + * @return {N/A} + */ function startAgent (confFile) { var monitor = slave({ confFile: confFile }) - + // check agent status var options = monitor.options - options.agent = options.agent || {} - if (options.agent.offline) { - console.error('Agent is offline, can not start it.') + if (options.agent && options.agent.offline) { + console.error('Agent is offline, fatal to start it.') return process.exit(0) } - options.port = options.port || 8088 + // socket.io server var sockio = socketIO() sockio.listen(options.port, { origins: options.origins || '*:*' @@ -87,19 +103,25 @@ function startAgent (confFile) { console.info('Socket.io server is listening on 0.0.0.0:' + options.port) } +/** + * Curses like dashboard + * @param {String} confFile full path of config file + * @return {N/A} + */ function dashboard (confFile) { // restore cursor - process.on('exit', function () { - process.stdout.write('\u001b[?25h') - }) + // process.on('exit', function () { + // process.stderr.write('\u001b[?25h') + // }) + // which server would you like to connect to. var monitor = slave({ confFile: confFile }) + var options = _.clone(monitor.options) var q = Monitor.available(options) - if (!q) { - console.error('No agent is online, can not start it.') + console.error('No agent is online, fatal to start it.') return process.exit(0) } var ql = q.choices.length @@ -117,40 +139,54 @@ function dashboard (confFile) { console.log('') inquirer.prompt(q).then(function (answers) { console.log('') + // connecting... _connectToDashboard(monitor, options, Monitor.parseConnectionString(answers.socket_server)) }) } +/** + * Exit process graceful + * @param {Number} code + * @param {String} signal + * @return {N/A} + */ function exitGraceful (code, signal) { code = code || 0 if (signal !== '-f') { - console.debug('Slave has exited, code: ' + code + ', signal: ' + (signal || 'NULL')) + console.debug('Slave has exited, code: ' + code + ', signal: ' + (signal || 'N/A')) } + // exit process after std flushed var fds = 0 + var stds = [process.stdout, process.stderr] + stds.forEach(function (std) { + var fd = std.fd + if (!std.bufferSize) { + fds = fds | fd + return + } + std.write && std.write('', function () { + fds = fds | fd + tryToExit() + }) + }) + tryToExit() function tryToExit () { if ((fds & 1) && (fds & 2)) { process.exit(code) } } - - [process.stdout, process.stderr].forEach(function (std) { - var fd = std.fd - if (!std.bufferSize) { - fds = fds | fd - } else { - std.write && std.write('', function () { - fds = fds | fd - tryToExit() - }) - } - }) - tryToExit() } +/** + * Spawn a slave-worker + * @param {Object} options + * @return {N/A} + */ function slave (options) { process.title = 'pm2-gui slave' options = options || {} + // check config file. var confFile = options.confFile if (!confFile) { confFile = path.resolve(__dirname, './pm2-gui.ini') @@ -160,28 +196,32 @@ function slave (options) { return process.exit(0) } } + + // initialize monitor. var monitor = Monitor({ confFile: confFile }) - + // initialize logger. Log(monitor.options.log) - + // logo console.log(chalk.cyan( '\n' + '█▀▀█ █▀▄▀█ █▀█ ░░ ▒█▀▀█ ▒█░▒█ ▀█▀\n' + '█░░█ █░▀░█ ░▄▀ ▀▀ ▒█░▄▄ ▒█░▒█ ▒█░\n' + '█▀▀▀ ▀░░░▀ █▄▄ ░░ ▒█▄▄█ ░▀▄▄▀ ▄█▄\n')) - + // listening signal (CTRL+C...) + process.on('uncaughtException', caughtException) process.on('SIGTERM', shutdown) process.on('SIGINT', shutdown) process.on('SIGHUP', restart) - process.on('uncaughtException', caughtException) process.on('exit', exitGraceful) + return monitor + function shutdown (code, signal) { console.info('Shutting down....') monitor.quit() - console.info('Shutdown complete!') + console.info('Completed!') exitGraceful(code, '-f') } @@ -191,40 +231,54 @@ function slave (options) { action: 'restart' }) } else { - console.error('No IPC found, could not restart monitor, shutting down.') + console.error('No IPC found, fatal to restart monitor') shutdown(1) } } function caughtException (err) { - console.error(err.stack) + process.stderr.write(chalk.red.bold('[PROCESS EXCEPTION]') + ' ' + err.stack + '\n') shutdown(1) } - - return monitor } +/** + * Connect to socket.io server and render the curses dashboard + * @param {Monitor} monitor + * @param {Object} options + * @param {Object} connection + * @return {N/A} + */ function _connectToDashboard (monitor, options, connection) { connection = _.extend({}, options, connection) - if (!!~['127.0.0.1', '0.0.0.0', 'localhost'].indexOf(connection.hostname)) { // eslint-disable-line no-extra-boolean-cast - return monitor.connect(connection, function (socket) { - console.info('Agent is online, try to connect it in dashboard directly.') - layout(connection).render(monitor) - }, function (err, socket) { - if (err === 'unauthorized') { - console.error('There was an error with the authentication:', err) - return process.exit(0) + if (regLocal.test(connection.hostname)) { + console.info('Connecting to local dashboard') + return monitor.connect(connection, function (err, socket) { + if (err) { + if (err === 'unauthorized') { + console.error('There was an error caught when verifying the auth-code:', err.message) + return process.exit(0) + } + console.warn('Fatal to connect to monitor:', err.message) + console.warn('Agent is offline, try to start it:', '127.0.0.1:' + connection.port) + // start socket.io server. + var sockio = socketIO() + sockio.listen(connection.port, { + origins: options.origins || '*:*' + }) + // run monitor + monitor.sockio = sockio + monitor.run() + // render dashboard + Layout(connection).render(monitor) + return } - console.warn('Agent is offline, try to start it.') - var sockio = socketIO() - sockio.listen(connection.port, { - origins: options.origins || '*:*' - }) - monitor.sockio = sockio - monitor.run() - layout(connection).render(monitor) + // render dashboard + console.info('Agent is online, try to connect it in dashboard directly.') + Layout(connection).render(monitor) }) } - layout(connection).render(monitor) + console.info('Connecting to remote dashboard') + Layout(connection).render(monitor) }