UNPKG

pm2

Version:

Production process manager for Node.JS applications with a built-in load balancer.

164 lines (143 loc) • 5.49 kB
/** * Copyright 2013-present the PM2 project authors. All rights reserved. * Use of this source code is governed by a license that * can be found in the LICENSE file. */ 'use strict'; /** * @file Cluster execution functions related * @author Alexandre Strzelewicz <as@unitech.io> * @project PM2 */ var cluster = require('cluster'); var path = require('path'); var Utility = require('../Utility.js'); var pkg = require('../../package.json'); var typestrip = require('../tools/typestrip.js'); var StdioLogger = require('./StdioLogger.js'); /** * Description * @method exports * @param {} God * @return */ module.exports = function ClusterMode(God) { /** * For Node apps - Cluster mode * It will wrap the code and enable load-balancing mode * @method nodeApp * @param {} env_copy * @param {} cb * @return Literal */ God.nodeApp = function nodeApp(env_copy, cb){ var clu = null; console.log(`App [${env_copy.name}:${env_copy.pm_id}] starting in -cluster mode-`) if (env_copy.node_args && Array.isArray(env_copy.node_args)) { cluster.settings.execArgv = env_copy.node_args; } // Cluster workers run on the daemon's node, whose version can differ // from the CLI that resolved the interpreter: ensure type stripping // is active for .ts apps when this node needs the flag if (path.extname(env_copy.pm_exec_path || '') === '.ts' && typestrip.supportLevel(process.versions.node) === 'flag' && cluster.settings.execArgv.indexOf('--experimental-strip-types') === -1) { cluster.settings.execArgv = cluster.settings.execArgv.concat('--experimental-strip-types'); } env_copy._pm2_version = pkg.version; // The worker's stdout/stderr are piped to the daemon (cluster silent // mode) instead of inheriting the daemon's fds: writes that bypass the // process.stdout.write hook of ProcessContainer.js (pino/sonic-boom, // children spawned with stdio 'inherit' like `npm start`...) used to // land in pm2.log (#3675 #4719 #4872 #4916 #4953 #5633) var stds = StdioLogger.stdsFromEnv(env_copy); // open() and not Utility.startLogging: files created here (as root) are // chowned to the app uid/gid the worker switches to before reopening them StdioLogger.open(stds, env_copy, function(err) { if (err) { God.logAndGenerateError(err); return cb(err); } try { // node.js cluster clients can not receive deep-level objects or arrays in the forked process, e.g.: // { "args": ["foo", "bar"], "env": { "foo1": "bar1" }} will be parsed to // { "args": "foo, bar", "env": "[object Object]"} // So we passing a stringified JSON here. clu = cluster.fork({pm2_env: JSON.stringify(env_copy), windowsHide: true}); } catch(e) { God.logAndGenerateError(e); StdioLogger.close(stds); return cb(e); } clu.pm2_env = env_copy; var stdio = StdioLogger.attach(God, clu.process, env_copy, stds); // Pipes closed and not the Worker 'exit': exit is relayed from ChildProcess // exit, which fires before the stdio pipes are drained. Data still in // flight (or written by an stdio-inherit child outliving the worker) // would hit closed streams and be dropped from the log files var closed = false; StdioLogger.onPipesClosed(clu.process, function cluCloseLogs() { closed = true; try { stdio.flush(); StdioLogger.close(stds); } catch(e) { God.logAndGenerateError(e); } }); // A stopped app stays in clusters_db: reloading its logs must not // reopen streams nobody will ever close (fd leak) clu._reloadLogs = function(cb) { if (closed) return cb(); try { StdioLogger.close(stds); } catch(e) { God.logAndGenerateError(e); } StdioLogger.open(stds, env_copy, function(err) { // worker closed while reopening if (closed) { try { StdioLogger.close(stds); } catch(e) {} } return cb(err); }); }; bindMessages(clu); return cb(null, clu); }); }; function bindMessages(clu) { /** * Broadcast message to God */ clu.on('message', function cluMessage(msg) { /********************************* * If you edit this function * Do the same in ForkMode.js ! *********************************/ if (msg.data && msg.type) { return God.bus.emit(msg.type ? msg.type : 'process:msg', { at : Utility.getDate(), data : msg.data, process : { pm_id : clu.pm2_env.pm_id, name : clu.pm2_env.name, rev : (clu.pm2_env.versioning && clu.pm2_env.versioning.revision) ? clu.pm2_env.versioning.revision : null, namespace : clu.pm2_env.namespace } }); } else { if (typeof msg == 'object' && 'node_version' in msg) { clu.pm2_env.node_version = msg.node_version; return false; } return God.bus.emit('process:msg', { at : Utility.getDate(), raw : msg, process : { pm_id : clu.pm2_env.pm_id, name : clu.pm2_env.name, namespace : clu.pm2_env.namespace } }); } }); } };