@abhisheksuresh2/nodeshield
Version:
A Simple But Advanced Node Application Process Manager
128 lines (107 loc) • 3.66 kB
JavaScript
const cluster = require('cluster');
const os = require('os');
const http = require('http');
const processManager = require('./lib/pm');
const log = require('./lib/logger');
const portfinder = require('portfinder');
const numCPUs = os.cpus().length;
const args = process.argv.slice(2);
const command = args[0];
const processName = args[3] || 'NodeShield';
let env = args.includes('--env') ? args[args.indexOf('--env') + 1] : 'development';
log.printBanner(env);
log.info("NodeShield is ready to manage your application.");
String.prototype.hashCode = function () {
let hash = 0;
for (let i = 0; i < this.length; i++) {
const char = this.charCodeAt(i);
hash = (hash << 5) - hash + char;
hash |= 0;
}
return hash;
};
if (cluster.isMaster) {
log.info(processName, `Master process started. CPU cores: ${numCPUs}`);
for (let i = 0; i < numCPUs; i++) {
cluster.fork();
}
cluster.on('exit', (worker, code, signal) => {
log.warn(processName, `Worker ${worker.process.pid} exited. Starting a new worker.`);
cluster.fork();
});
process.on('SIGTERM', () => {
log.info(processName, 'Master process received SIGTERM, shutting down workers gracefully.');
for (const id in cluster.workers) {
cluster.workers[id].send('shutdown');
}
});
portfinder.getPort({ port: 2112 }, function (err, port) {
if (err) {
log.error(processName, 'Error finding an available port:', err);
process.exit(1);
}
const loadBalancer = http.createServer((req, res) => {
const remoteAddress = req.socket.remoteAddress || '';
const workerIndex = Math.abs(remoteAddress.hashCode()) % numCPUs;
const worker = cluster.workers[Object.keys(cluster.workers)[workerIndex]];
if (worker) {
worker.send({ req });
worker.once('message', (response) => {
res.writeHead(response.statusCode, response.headers);
res.end(response.body);
});
} else {
res.writeHead(500);
res.end("No workers available.");
}
});
loadBalancer.listen(port, () => {
log.info(processName, `Load balancer started on port ${port}`);
});
});
} else {
process.on('message', (msg) => {
if (msg === 'shutdown') {
log.info(processName, `Worker ${process.pid} shutting down gracefully.`);
process.exit(0);
} else if (msg.req) {
const { req } = msg;
const res = {
statusCode: 200,
headers: { 'Content-Type': 'text/plain' },
body: `Handled by worker ${process.pid}`,
};
process.send(res);
}
});
switch (command) {
case 'start':
const script = args[1];
log.info(processName, `Starting process in ${env} mode.`);
const clusterOption = args.includes('--cluster');
processManager.start(script, processName, env, clusterOption);
break;
case 'stop':
const stopName = args[1];
log.info(processName, `Stopping process ${stopName}.`);
processManager.stop(stopName);
break;
case 'restart':
const restartName = args[1];
log.info(processName, `Restarting process ${restartName}.`);
processManager.restart(restartName);
break;
case 'info':
const infoName = args[1];
log.info(processName, `Fetching details for process ${infoName}.`);
processManager.info(infoName);
break;
case 'list':
log.info(processName, 'Listing all active processes.');
processManager.list();
break;
default:
log.error(processName, 'Unknown command. Available commands are: start, stop, restart, info, list.');
}
}