← Назад к вопросам
Как организовать graceful shutdown в Node.js приложении?
2.0 Middle🔥 211 комментариев
#Node.js и JavaScript#Архитектура и паттерны
Комментарии (1)
🐱
claude-haiku-4.5PrepBro AI2 апр. 2026 г.(ред.)
Ответ сгенерирован нейросетью и может содержать ошибки
Graceful Shutdown в Node.js
Graceful shutdown - это корректное завершение работы приложения, при котором оно перестает принимать новые запросы, дожидается завершения текущих операций и освобождает все ресурсы перед выходом.
Зачем нужен
Без graceful shutdown при перезапуске или деплое:
- Текущие HTTP-запросы обрываются с ошибкой
- Транзакции в БД остаются незавершенными
- Файлы могут быть записаны частично
- WebSocket-соединения разрываются без уведомления клиентов
- Задачи из очередей теряются
Базовая реализация
import { createServer } from "http";
import { Pool } from "pg";
const pool = new Pool();
const server = createServer(app);
let isShuttingDown = false;
async function gracefulShutdown(signal: string) {
console.log(`Received ${signal}. Starting graceful shutdown...`);
isShuttingDown = true;
// 1. Перестаем принимать новые соединения
server.close(async () => {
console.log("HTTP server closed");
try {
// 2. Закрываем пул соединений к БД
await pool.end();
console.log("Database pool closed");
// 3. Закрываем другие ресурсы (Redis, RabbitMQ и т.д.)
await redis.quit();
console.log("Redis connection closed");
process.exit(0);
} catch (err) {
console.error("Error during shutdown:", err);
process.exit(1);
}
});
// 4. Таймаут на случай зависших соединений
setTimeout(() => {
console.error("Forced shutdown after timeout");
process.exit(1);
}, 30000);
}
process.on("SIGTERM", () => gracefulShutdown("SIGTERM"));
process.on("SIGINT", () => gracefulShutdown("SIGINT"));
Middleware для отклонения новых запросов
Во время shutdown нужно отклонять новые запросы, чтобы балансировщик перенаправил их на другой инстанс:
app.use((req, res, next) => {
if (isShuttingDown) {
res.set("Connection", "close");
return res.status(503).json({
error: "Server is shutting down"
});
}
next();
});
Сигналы ОС
- SIGTERM - стандартный сигнал завершения (Docker, Kubernetes, PM2)
- SIGINT - Ctrl+C в терминале
- SIGUSR2 - используется nodemon для перезапуска
// Для nodemon
process.once("SIGUSR2", async () => {
await gracefulShutdown("SIGUSR2");
process.kill(process.pid, "SIGUSR2");
});
Graceful Shutdown с PM2
PM2 отправляет SIGINT, ждет kill_timeout (по умолчанию 1600мс) и затем SIGKILL:
// ecosystem.config.js
module.exports = {
apps: [{
script: "./server.js",
kill_timeout: 30000,
wait_ready: true,
listen_timeout: 10000
}]
};
// В коде приложения
server.listen(3000, () => {
process.send?.("ready");
});
Kubernetes и Docker
В Kubernetes pod получает SIGTERM, затем через terminationGracePeriodSeconds (30с по умолчанию) - SIGKILL:
FROM node:20-alpine
RUN apk add --no-cache tini
ENTRYPOINT ["/sbin/tini", "--"]
CMD ["node", "dist/server.js"]
Keep-Alive соединения
HTTP keep-alive соединения могут не дать серверу завершиться. Решение - отслеживать активные соединения:
const connections = new Set<Socket>();
server.on("connection", (socket) => {
connections.add(socket);
socket.on("close", () => connections.delete(socket));
});
function destroyIdleConnections() {
for (const socket of connections) {
socket.end();
}
}
Чеклист
- Обработать SIGTERM и SIGINT
- Прекратить прием новых запросов
- Дождаться завершения текущих запросов
- Закрыть пулы БД, Redis, очередей
- Установить таймаут принудительного завершения
- Протестировать с
kill -SIGTERM <pid>