pm2
Version:
Production process manager for Node.JS applications with a built-in load balancer.
164 lines (143 loc) • 5.49 kB
JavaScript
/**
* 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.
*/
;
/**
* @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
}
});
}
});
}
};