Node.js''in ünlü single-threaded event loop''u hem güçlü yönü hem zayıf noktasıdır. Tek bir blocking operasyon tüm uygulamayı dondurur. Bu yazı Node.js''in internal''ını anlayıp performans sorunlarını nasıl tespit edip çözeceğinizi üretim örnekleriyle gösterir.

Event Loop Anatomisi

İlgili rehberler: Yazılım geliştirme süreçleri · PostgreSQL optimizasyonu · Git ileri seviye komutlar · Redis nedir, nasıl kullanılır · Docker ile deploy

Event loop 6 fazdan oluşur: timers (setTimeout), pending callbacks, idle/prepare, poll (I/O), check (setImmediate), close callbacks. Her iterasyon bu fazları sırayla dolaşır. process.nextTick() ve Promise microtask''ları her faz arasında boşaltılır.

// Tipik sıralama örneği
setTimeout(() => console.log('1 timeout'), 0);
setImmediate(() => console.log('2 immediate'));
Promise.resolve().then(() => console.log('3 promise'));
process.nextTick(() => console.log('4 nextTick'));
console.log('5 sync');

// Çıktı:
// 5 sync
// 4 nextTick
// 3 promise
// 1 timeout  (veya 2 immediate — sıra OS''e bağlı)
// 2 immediate

Blocking Operations

Event loop''u bloke eden 4 düşman: büyük JSON parse, sync file I/O, CPU-intensive hesaplama, regex backtracking. Biri çalışırken yeni istek karşılanamaz.

// Olay: /api/report endpoint'i 30 saniye sürüyor. TÜM kullanıcılar bloke oluyor.

// Kötü: CPU ağır işi event loop'ta
app.get('/report', (req, res) => {
    const result = calculateHeavyReport();  // 30 sn CPU
    res.json(result);
});

// İyi: Worker thread'e taşı
const { Worker } = require('worker_threads');
app.get('/report', (req, res) => {
    const worker = new Worker('./report-worker.js');
    worker.postMessage({ filters: req.query });
    worker.on('message', result => res.json(result));
    worker.on('error', err => res.status(500).json({ error: err.message }));
});

// Daha iyi: Queue'ya at, async işle
// Bull, BullMQ, Agenda
queue.add('generate-report', { filters: req.query });
res.status(202).json({ status: 'queued', pollUrl: '/report/:id' });

Event Loop Gecikmesi Ölçmek

// Basit event loop lag tracker
setInterval(() => {
    const start = Date.now();
    setImmediate(() => {
        const lag = Date.now() - start;
        if (lag > 50) {
            console.warn(`Event loop lag: ${lag}ms`);
        }
    });
}, 1000);

// Daha iyisi: @isaacs/lag
const lag = require('event-loop-lag')(1000);
setInterval(() => {
    console.log(`lag: ${lag()} ms`);
}, 5000);
İpucu
Event loop lag 50ms üstüne çıkıyorsa blocking bir operasyon var. 200ms+ ise kullanıcılar fark eder.

Memory Leak Tespiti

Node.js memory leak kaynakları: global map/cache büyümesi, event listener biriktirme, closure''da tutulan büyük object, timer temizlenmemesi.

// KLASIK LEAK
const cache = new Map();
app.get('/api/:id', (req, res) => {
    cache.set(req.params.id, req.body);  // LRU yok, sınırsız büyür
    res.send('ok');
});

// DÜZELTME: LRU cache
const LRU = require('lru-cache');
const cache = new LRU({ max: 10000, ttl: 1000 * 60 * 10 });
// LEAK 2: Event listener
emitter.on('data', handler);  // her istekte yeni listener!

// DÜZELTME
if (emitter.listenerCount('data') === 0) emitter.on('data', handler);
// ya da
emitter.once('data', handler);

Heap Snapshot

# Node başlat inspector ile
node --inspect server.js

# Chrome'da chrome://inspect aç → Remote Target → inspect
# Memory tab → Heap snapshot
# 5 dakika arayla iki snapshot al, karşılaştır
# Hangi class'ların instance sayısı artıyor? = leak

Clinic.js ile Profiling

npm i -g clinic

# CPU profil
clinic doctor -- node server.js
# 30 saniye trafik gönder (autocannon ile)
autocannon http://localhost:3000 -c 100 -d 30
# Ctrl+C → HTML raporu açılır

# Flame graph (hangi fonksiyon CPU yakıyor)
clinic flame -- node server.js

# Bubbleprof (async flow)
clinic bubbleprof -- node server.js

Benchmark: autocannon

npm i -g autocannon

# 100 eşzamanlı bağlantı, 30 sn
autocannon -c 100 -d 30 http://localhost:3000/api/users

# POST ile
autocannon -c 50 -d 10 -m POST -H 'Content-Type: application/json' \
    -b '{"email":"test@x.com"}' http://localhost:3000/api/login

V8 Optimizasyonları

  • Hidden class — object''in şekli değişmesin, tüm field''ları constructor''da tanımla
  • Monomorphic function — aynı fonksiyona hep aynı tipte parametre geç
  • Inlining — küçük fonksiyonlar inline edilir, büyük fonksiyonları parçala
  • Deoptimizationtry/catch ağır döngü içinde V8 optimize edemez

Bellek ve CPU Limitleri

# Node default heap limit 1.7GB
# Büyük uygulamada artır
node --max-old-space-size=4096 server.js

# V8 GC trace (debug için)
node --trace-gc server.js 2>&1 | grep 'Mark-Compact'

# CPU profiling flag
node --prof server.js
# sonra:
node --prof-process isolate-*.log > profile.txt

Hızlı Kazanımlar

  • keep-alive — dış HTTP istekleri için agent reuse
  • JSON.parse yerine simdjson — 5x hızlı
  • Streaming — büyük dosyaları memory''de tutma
  • DB connection pool — her istek yeni bağlantı açmasın
  • gzip — response boyutunu %70 düşür
  • DNS cache — Node default DNS cache yok, cacheable-lookup ekle

Modern Yazılım Geliştirme ve DevOps Pratikleri

Profesyonel yazılım geliştirme süreci üç pillar üzerine kuruludur: kaynak kontrolü (Git + GitHub/GitLab pull request akışı, code review zorunlu), CI/CD pipeline (otomatik test + lint + build + deploy), ve gözlemlenebilirlik (Sentry/Datadog/Grafana ile log, metric, trace toplama). Test piramidi (unit > integration > e2e) ile kod kalitesini garantilemek, mikroservis mimarisinde Docker container ve Kubernetes orkestrasyonu kullanmak, REST veya GraphQL API tasarımında OpenAPI/GraphQL Schema sözleşmesi tutmak modern standardlardır. Yazılım geliştirme yaşam döngüsü boyunca (gereksinim → tasarım → implementasyon → test → deploy → bakım) Agile/Scrum sprintleri 1-2 hafta, DevOps takımları sürekli teslim (continuous delivery) prensibiyle çalışır.

Node.js performans optimizasyonu

Mevcut uygulamanızın profiling''i, memory leak tespiti ve optimizasyon planı için bize yazın

WhatsApp