Когда возникает необходимость завершить процесс Node.js, который обрабатывает множество запросов, важно сделать это корректно, чтобы:
Graceful Shutdown - это процесс корректного завершения работы приложения, который включает:
process.on('SIGTERM', () => {
console.log('Получен SIGTERM. Инициирую Graceful Shutdown');
// 1. Прекращаем принимать новые соединения
server.close(() => {
console.log('Все соединения закрыты');
// 2. Закрываем подключения к БД, кэшу и т.д.
database.disconnect().then(() => {
// 3. Завершаем процесс
process.exit(0);
});
});
// Принудительное завершение по таймауту
setTimeout(() => {
console.error('Принудительное завершение по таймауту');
process.exit(1);
}, 5000);
});
При работе с кластером нужно учитывать завершение всех воркеров:
const cluster = require('cluster');
if (cluster.isMaster) {
// Обработка сигналов в мастер-процессе
process.on('SIGTERM', () => {
const workers = Object.values(cluster.workers);
workers.forEach(worker => {
worker.send('shutdown');
worker.disconnect();
setTimeout(() => {
if (!worker.isDead()) worker.kill();
}, 5000);
});
});
} else {
// Обработка в воркере
process.on('message', (msg) => {
if (msg === 'shutdown') {
gracefulShutdown();
}
});
}
Для HTTP-сервера важно:
const server = app.listen(3000);
const gracefulShutdown = () => {
// Закрываем сервер для новых соединений
server.close(() => {
// Дополнительная очистка
redisClient.quit();
dbConnection.end();
process.exit(0);
});
// Закрываем все keep-alive соединения
server.closeIdleConnections();
};
Для отслеживания активных запросов:
let activeRequests = 0;
app.use((req, res, next) => {
activeRequests++;
res.on('finish', () => activeRequests--);
next();
});
const shutdown = () => {
server.close(() => {
const interval = setInterval(() => {
if (activeRequests === 0) {
clearInterval(interval);
process.exit(0);
}
}, 100);
});
};
Профессиональные решения:
pm2 gracefulReload
)# Пример с PM2
pm2 reload app --update-env
Для непредвиденных ошибок:
process.on('uncaughtException', (err) => {
logger.fatal(err);
// Даем время на логирование
setTimeout(() => {
process.exit(1);
}, 1000);
});
Важно для оркестраторов:
app.get('/health', (req, res) => {
if (shuttingDown) {
return res.status(503).end();
}
res.status(200).json({ status: 'OK' });
});
Для безопасного завершения процесса Node.js необходимо: