Graceful Shutdown в Node.js

Корректное завершение процесса: дослушать активные запросы, закрыть соединения с БД, сбросить буферы логов — потом process.exit(0). Альтернатива — потеря данных и битые сессии.

Зачем нужно

В продакшене процесс рестартится десятки раз в день: deploy, OOM, kubernetes scale, systemd reload. Если просто убить процесс (SIGKILL), то:

  • активные HTTP-запросы оборвутся на полпути — клиент получит ECONNRESET
  • транзакция в БД может остаться незавершённой
  • буфер логов не сбросится — последние события потеряются
  • WebSocket-клиенты не получат сигнал disconnect

Сигналы

  • SIGTERM — «вежливая» просьба завершиться (kubectl, systemctl, docker stop). Обрабатываем.
  • SIGINT — Ctrl+C. Обрабатываем как SIGTERM в dev.
  • SIGKILLkill -9, не перехватывается. Если до него дошло — что-то пошло не так.
  • SIGHUP — закрытие терминала, обычно «перечитай конфиг».

Пример

const http = require('http');

const server = http.createServer(handler);
server.listen(3000);

let shuttingDown = false;

async function shutdown(signal) {
  if (shuttingDown) return;
  shuttingDown = true;
  console.log(`Received ${signal}, shutting down...`);

  // 1. Перестать принимать новые соединения
  server.close( => console.log('HTTP closed'));

  // 2. Дать активным запросам ~30 секунд
  const timeout = setTimeout(() => {
    console.error('Forced shutdown');
    process.exit(1);
  }, 30_000);
  timeout.unref;

  // 3. Закрыть БД, очереди, логи
  await db.close();
  await logger.flush;

  process.exit(0);
}

process.on('SIGTERM', () => shutdown('SIGTERM'));
process.on('SIGINT', () => shutdown('SIGINT'));
process.on('uncaughtException', (err) => {
  console.error(err);
  shutdown('uncaughtException');
});

Подводные камни

  • Health check должен ОТВЕЧАТЬ unhealthy сразу после получения SIGTERM — иначе load balancer продолжит слать трафик
  • Долгие операции (миграция, large query) могут не успеть за timeout — выбирай разумное окно
  • Kubernetes даёт terminationGracePeriodSeconds (по умолчанию 30s) — синхронизируй
  • PM2 в cluster mode сам шлёт SIGTERM воркерам и ждёт wait_ready события
  • Двойной shutdown — флаг shuttingDown обязателен, иначе несколько одновременных сигналов вызовут гонки

🎓 Источники

  • 🎓 [Graceful Shutdown в Node.js] · 2019-09-12 · YouTube · [Marp](../../../Documents/TimurShemsedinov/2019-09-12 — Graceful Shutdown в Node.js (ZstnowFeCe0).md)
    • Тезисы: перехват SIGTERM/SIGINT, последовательное закрытие server/db/queue/log, таймаут на принудительное завершение, корректное завершение worker_threads и cluster воркеров

См. также