Process Management
Process management controls how Node.js applications run, restart, and handle resources. Learn to use process managers for production deployment and monitoring.
Challenge
Section titled “Challenge”Implement reliable process management that handles crashes, restarts, resource limits, and zero-downtime deployments.
Explanation
Section titled “Explanation”Why Process Management?
Section titled “Why Process Management?”Crash Handling: Automatic restart on failure Resource Management: Monitor and limit CPU, memory Zero-Downtime Deployment: Graceful restarts Clustering: Multi-core utilization Logging: Centralized process output
Popular Process Managers
Section titled “Popular Process Managers”- PM2: Most popular, feature-rich
- Forever: Simple, lightweight
- Systemd: OS-level management
- Docker: Container-based management
- Kubernetes: Orchestration platform
Code Examples
Section titled “Code Examples”1. PM2 Configuration
Section titled “1. PM2 Configuration”module.exports = { apps: [ { name: 'app', script: './server.js', instances: 'max', exec_mode: 'cluster',
// Environment env: { NODE_ENV: 'development', }, env_production: { NODE_ENV: 'production', },
// Resource limits max_memory_restart: '500M',
// Restart configuration watch: ['src'], watch_delay: 1000, ignore_watch: ['node_modules', 'logs'],
// Graceful shutdown kill_timeout: 5000, listen_timeout: 10000,
// Error handling error_file: './logs/error.log', out_file: './logs/out.log', log_file: './logs/combined.log',
// Merge logs merge_logs: true,
// Restart policy max_restarts: 10, min_uptime: '10s',
// Autorestart on file changes watch: true, ext_type: 'js,json', }, ],};2. Cluster Module for Multi-Core
Section titled “2. Cluster Module for Multi-Core”const cluster = require('cluster');const os = require('os');const express = require('express');
const numCores = os.cpus().length;
if (cluster.isMaster) { console.log(`Master process ${process.pid} is running`);
// Fork workers for (let i = 0; i < numCores; i++) { cluster.fork(); }
// Handle worker death cluster.on('exit', (worker, code, signal) => { console.log(`Worker ${worker.process.pid} died`); // Restart worker cluster.fork(); });
// Graceful shutdown process.on('SIGTERM', () => { console.log('SIGTERM received, shutting down gracefully');
const workers = Object.values(cluster.workers); workers.forEach(worker => worker.kill()); });} else { const app = express();
app.get('/', (req, res) => { res.json({ message: 'Hello', pid: process.pid, }); });
app.listen(3000, () => { console.log(`Worker ${process.pid} started`); });}3. Graceful Shutdown Handler
Section titled “3. Graceful Shutdown Handler”class GracefulShutdown { constructor(server, options = {}) { this.server = server; this.timeout = options.timeout || 30000; this.callbacks = []; }
onShutdown(callback) { this.callbacks.push(callback); }
async shutdown(signal) { console.log(`${signal} received, starting graceful shutdown`);
// Stop accepting new requests this.server.close(() => { console.log('HTTP server closed'); });
// Close existing connections const activeConnections = new Set();
this.server.on('connection', (conn) => { activeConnections.add(conn); conn.on('close', () => activeConnections.delete(conn)); });
// Wait for active connections const shutdownTimeout = setTimeout(() => { console.error('Shutdown timeout exceeded'); activeConnections.forEach(conn => conn.destroy()); }, this.timeout);
// Run callbacks for (const callback of this.callbacks) { try { await callback(); } catch (error) { console.error('Shutdown callback error:', error); } }
clearTimeout(shutdownTimeout); process.exit(0); }
register() { process.on('SIGTERM', () => this.shutdown('SIGTERM')); process.on('SIGINT', () => this.shutdown('SIGINT')); }}
// Usageconst express = require('express');const app = express();const server = app.listen(3000);
const shutdown = new GracefulShutdown(server, { timeout: 30000 });
// Register cleanup callbacksshutdown.onShutdown(async () => { console.log('Closing database connection'); await database.close();});
shutdown.onShutdown(async () => { console.log('Closing cache connection'); await cache.disconnect();});
shutdown.register();4. Process Health Monitoring
Section titled “4. Process Health Monitoring”class ProcessMonitor { constructor(options = {}) { this.checkInterval = options.checkInterval || 5000; this.memoryThreshold = options.memoryThreshold || 500 * 1024 * 1024; // 500MB this.cpuThreshold = options.cpuThreshold || 80; // percent }
start() { setInterval(() => { this.checkHealth(); }, this.checkInterval); }
checkHealth() { const memUsage = process.memoryUsage(); const heapPercent = (memUsage.heapUsed / memUsage.heapTotal) * 100;
// Memory check if (memUsage.heapUsed > this.memoryThreshold) { console.warn('High memory usage:', { heapUsed: Math.round(memUsage.heapUsed / 1024 / 1024) + 'MB', heapTotal: Math.round(memUsage.heapTotal / 1024 / 1024) + 'MB', heapPercent: heapPercent.toFixed(2) + '%', });
// Trigger garbage collection if (global.gc) { global.gc(); }
// Restart if critical if (heapPercent > 95) { this.gracefulRestart(); } }
// CPU check const usage = process.cpuUsage(); const totalCPU = (usage.user + usage.system) / 1000000; // seconds console.log('CPU usage:', totalCPU.toFixed(2) + 's'); }
gracefulRestart() { console.log('Initiating graceful restart due to resource limits'); process.exit(0); // Let process manager restart }}
const monitor = new ProcessMonitor({ memoryThreshold: 300 * 1024 * 1024, checkInterval: 10000,});
monitor.start();5. Process Communication
Section titled “5. Process Communication”const cluster = require('cluster');
if (cluster.isMaster) { const workers = [];
// Create workers for (let i = 0; i < 2; i++) { const worker = cluster.fork(); workers.push(worker); }
// Send message to all workers function broadcastMessage(message) { workers.forEach(worker => { worker.send(message); }); }
// Handle worker messages Object.values(cluster.workers).forEach(worker => { worker.on('message', (msg) => { console.log('Master received:', msg);
if (msg.type === 'task-complete') { // Broadcast completion to other workers broadcastMessage({ type: 'task-result', data: msg.data, }); } }); });} else { // Worker process process.on('message', (msg) => { console.log('Worker received:', msg);
if (msg.type === 'start-task') { // Do work process.send({ type: 'task-complete', data: 'Task finished', }); } });
console.log(`Worker ${process.pid} started`);}6. Zero-Downtime Deployment
Section titled “6. Zero-Downtime Deployment”const express = require('express');const http = require('http');
class ZeroDowntimeServer { constructor(app, port = 3000) { this.app = app; this.port = port; this.server = null; this.activeConnections = new Set(); this.isShuttingDown = false; }
start() { this.server = http.createServer(this.app);
this.server.on('connection', (connection) => { this.activeConnections.add(connection);
connection.on('close', () => { this.activeConnections.delete(connection); });
// Close connection after timeout during shutdown if (this.isShuttingDown) { connection.setTimeout(5000, () => connection.destroy()); } });
this.registerShutdownHandlers();
return new Promise((resolve) => { this.server.listen(this.port, () => { console.log(`Server listening on port ${this.port}`); resolve(); }); }); }
registerShutdownHandlers() { const signals = ['SIGTERM', 'SIGINT'];
signals.forEach(signal => { process.on(signal, async () => { console.log(`Received ${signal}, starting graceful shutdown`); await this.shutdown(); }); }); }
async shutdown() { if (this.isShuttingDown) return; this.isShuttingDown = true;
// Stop accepting new requests this.server.close();
// Wait for active connections const shutdownTimeout = new Promise(resolve => setTimeout(resolve, 30000) );
await Promise.race([ new Promise(resolve => { const check = setInterval(() => { if (this.activeConnections.size === 0) { clearInterval(check); resolve(); } }, 1000); }), shutdownTimeout, ]);
// Force close remaining connections this.activeConnections.forEach(connection => connection.destroy());
process.exit(0); }}
// Usageconst app = express();const server = new ZeroDowntimeServer(app, 3000);server.start();Best Practices
Section titled “Best Practices”-
Configuration
- Use process manager configuration files
- Separate dev and production configs
- Version control config files
- Document all options
-
Monitoring
- Monitor memory and CPU usage
- Set resource limits
- Alert on process restarts
- Log all process events
-
Graceful Shutdown
- Handle signals properly
- Close resources cleanly
- Set shutdown timeouts
- Drain connection queues
-
High Availability
- Run multiple instances
- Load balance traffic
- Health check endpoints
- Auto-restart on failure
-
Deployment
- Zero-downtime deployments
- Rolling restarts
- Backward compatibility
- Quick rollback capability
Real-World Applications
Section titled “Real-World Applications”- Production Servers: PM2 for clustering
- Cloud Deployments: Docker with orchestration
- CI/CD Pipelines: Automated restarts
- Microservices: Independent process management
- Load Balancing: Multi-process distribution