← Назад к вопросам

Как организовать 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>
Как организовать graceful shutdown в Node.js приложении? | PrepBro