02.01.2026

Node.js’te Graceful Shutdown: Sıfır Kesinti İçin Kapanışı Doğru Tasarlamak

SIGTERM/SIGINT yakalayarak bağlantıları güvenle kapatın, istekleri tamamlayın ve veri kaybını önleyin.

Node.js uygulamaları çoğunlukla container (Docker/Kubernetes) içinde çalışır. Deploy sırasında pod’lar SIGTERM alır, load balancer yeni istek göndermeyi keser ve uygulamanın “temiz” şekilde kapanması beklenir. Bu süreci doğru yönetmezseniz yarım kalan HTTP istekleri, boşa düşen mesajlar ve kirli bağlantılar kaçınılmaz olur.

Neden Graceful Shutdown?

  • Yarım kalan istekleri tamamlamak (özellikle ödeme, sipariş gibi kritik akışlar)
  • DB/Redis bağlantılarını düzgün kapatmak
  • Queue tüketicilerini durdurmak (yeniden deneme fırtınası yaşamamak)
  • Kubernetes terminationGracePeriodSeconds süresini verimli kullanmak

Temel yaklaşım

  1. SIGTERM/SIGINT sinyallerini yakala
  2. HTTP sunucusunu yeni bağlantılara kapat (server.close)
  3. Aktif istekleri takip et ve bitmelerini bekle
  4. Kaynakları sırayla kapat (DB, Redis, mesaj tüketicisi vs.)
  5. Zaman aşımıyla zorla çık (son çare)

Uygulama: Express + aktif istek takibi

Aşağıdaki örnek, yeni istek alımını durdurur, mevcut istekleri bekler, belirli süreyi aşarsa aktif socket’leri sonlandırır.

import express from "express";
import http from "http";

const app = express();
app.get("/", async (req, res) => {
  // Örnek: kısa bir iş
  await new Promise(r => setTimeout(r, 300));
  res.json({ ok: true });
});

const server = http.createServer(app);

// Aktif socket ve istek takibi
const sockets = new Set();
let activeRequests = 0;
let isShuttingDown = false;

server.on("connection", (socket) => {
  sockets.add(socket);
  socket.on("close", () => sockets.delete(socket));
});

app.use((req, res, next) => {
  activeRequests++;
  res.on("finish", () => activeRequests--);

  if (isShuttingDown) {
    // Kapanış sırasında yeni request kabul etmeyelim
    res.set("Connection", "close");
  }

  next();
});

server.listen(3000, () => console.log("Listening :3000"));

async function shutdown(signal) {
  if (isShuttingDown) return;
  isShuttingDown = true;

  console.log(`Received ${signal}. Starting graceful shutdown...`);

  // 1) Yeni bağlantıları kapat
  server.close(() => {
    console.log("HTTP server closed (no longer accepting new connections)." );
  });

  // 2) Aktif isteklerin bitmesini bekle (maks. 10 sn)
  const deadlineMs = 10_000;
  const start = Date.now();

  while (activeRequests > 0 && Date.now() - start < deadlineMs) {
    await new Promise(r => setTimeout(r, 100));
  }

  // 3) Hâlâ istek varsa socket'leri sonlandır
  if (activeRequests > 0) {
    console.warn(`Timeout. Forcing close. Active requests: ${activeRequests}`);
    for (const s of sockets) s.destroy();
  }

  // 4) Burada DB/Redis/Queue kapatma adımlarını çağırın
  // await db.close();
  // await redis.quit();
  // await consumer.stop();

  process.exit(0);
}

process.on("SIGTERM", () => shutdown("SIGTERM"));
process.on("SIGINT", () => shutdown("SIGINT"));

Dikkat edilmesi gereken noktalar

  • Health endpoint’leri ayırın: Kapanış sırasında readiness’i “false” yapıp trafiği üzerinizden aldırın.
  • Keep-Alive etkisi: Bazı client’lar bağlantıyı tutar; bu yüzden socket takibi önemlidir.
  • Zorla kapatma planı: Her zaman bir “son tarih” (deadline) belirleyin.
  • Log’ları kaybetmeyin: Çıkmadan önce log buffer/transport flush etmek gerekebilir.

Kapanış

Graceful shutdown, “sorun çıkınca bakarız” denecek bir detay değil; üretimde kesinti ve veri tutarlılığı açısından kritik bir yapı taşıdır. Uygulamanızın kapanma akışını test edin: deploy sırasında, SIGTERM ile ve yük altındayken.