Node.js Performansı: Event Loop, Kümeleme ve Optimizasyon 2026
Event loop yönetimi, kümeleme stratejileri ve worker thread'ler ile Node.js performans optimizasyonu. 2026'da yüksek verimli Node.js uygulamaları için pratik kalıplar.

Node.js performans optimizasyonu, event loop'u anlamakla başlar — tüm asenkron I/O işlemlerini yöneten tek iş parçacıklı mekanizma. Node.js 22 LTS veya Node.js 24 çalıştıran üretim sistemlerinde, kötü event loop yönetimi gecikme artışlarının, düşen bağlantıların ve yük altında kademeli arızaların başlıca nedeni olmaya devam etmektedir.
Bu rehber, Node.js performansının üç temel direğini kapsar: event loop iç yapısı, cluster ve worker thread'ler ile çok çekirdekli ölçekleme ve yüksek verimli sistemlerde kullanılan pratik optimizasyon kalıpları.
Event loop, callback'leri katı bir faz sırasında işler: zamanlayıcılar, bekleyen callback'ler, idle/prepare, poll, check ve close callback'leri. Herhangi bir fazın bloke edilmesi tüm uygulamayı durdurur. Event loop kullanımını performance.eventLoopUtilization() ile izlemek, Node.js performans sorunları için en etkili teşhis aracıdır.
Node.js Event Loop İstekleri Nasıl İşler
Event loop basit bir kuyruk değildir. Her biri belirli bir callback kategorisinden sorumlu altı ayrı fazda çalışır. Bu faz mimarisini anlamak, belirli kalıpların neden gecikmeye neden olduğunu, diğerlerinin ise neden olmadığını açıklar.
// Demonstrating phase execution order
const fs = require('fs');
// Phase 1: Timers — executes setTimeout/setInterval callbacks
setTimeout(() => console.log('1. Timer phase'), 0);
// Phase 4: Poll — executes I/O callbacks
fs.readFile(__filename, () => {
console.log('2. Poll phase (I/O callback)');
// Phase 5: Check — executes setImmediate callbacks
setImmediate(() => console.log('3. Check phase (setImmediate)'));
// Phase 1 again: Timer scheduled from within I/O
setTimeout(() => console.log('4. Timer phase (from I/O)'), 0);
});
// Microtask — runs between every phase transition
Promise.resolve().then(() => console.log('Microtask: Promise'));
process.nextTick(() => console.log('Microtask: nextTick'));Çıktı sırası faz önceliğini ortaya koyar: nextTick promise'lerden önce çalışır, promise'ler zamanlayıcılardan önce çalışır ve setImmediate her zaman check fazında I/O callback'lerinden sonra tetiklenir. Bu sıralama, gecikmeye duyarlı istek işleyicileri tasarlanırken önem kazanır.
Üretimde Event Loop Bloklama Tespiti
Bloklanmış bir event loop, ortalama yanıt süreleri bozulmadan çok önce artan p99 gecikme olarak kendini gösterir. Node.js 16'dan bu yana kararlı olan yerleşik performance.eventLoopUtilization() API'si, en güvenilir tespit mekanizmasını sağlar.
// Production-grade event loop monitoring
const { performance, monitorEventLoopDelay } = require('perf_hooks');
// High-resolution event loop delay histogram
const histogram = monitorEventLoopDelay({ resolution: 20 });
histogram.enable();
// Track utilization over intervals
let previous = performance.eventLoopUtilization();
setInterval(() => {
const current = performance.eventLoopUtilization(previous);
previous = performance.eventLoopUtilization();
const metrics = {
// Ratio of time the loop spent active vs idle (0-1)
utilization: current.utilization.toFixed(3),
// Delay percentiles in milliseconds
p50: (histogram.percentile(50) / 1e6).toFixed(2),
p99: (histogram.percentile(99) / 1e6).toFixed(2),
max: (histogram.max / 1e6).toFixed(2),
};
// Alert when utilization exceeds 70% or p99 > 100ms
if (current.utilization > 0.7 || histogram.percentile(99) > 100e6) {
console.warn('EVENT_LOOP_SATURATED', metrics);
}
histogram.reset();
}, 5000);Kullanım oranının 0.7'nin (%70) üzerine çıkması, event loop'un I/O beklemekten çok callback yürütmeye zaman harcadığını gösterir. Bu eşikte gelen bağlantılar kuyrukta beklemeye başlar ve kuyruk sonu gecikmeleri üstel olarak artar.
Yaygın Event Loop Bloklayıcıları ve Çözümleri
Üç kalıp, üretim Node.js uygulamalarında event loop bloklama olaylarının çoğunluğunu oluşturur: büyük yüklerde senkron JSON işlemleri, istek işleyicilerinde CPU yoğun hesaplamalar ve sınırsız düzenli ifadeler.
// Anti-patterns and their solutions
// PROBLEM: JSON.parse blocks on large payloads
const largePayload = Buffer.alloc(50 * 1024 * 1024); // 50MB
// JSON.parse(largePayload.toString()); // Blocks event loop 200-500ms
// SOLUTION: Stream-parse large JSON with a streaming parser
const { Transform } = require('stream');
const JSONStream = require('jsonstream2');
function processLargeJSON(readableStream) {
return new Promise((resolve, reject) => {
const results = [];
readableStream
.pipe(JSONStream.parse('items.*')) // Stream-parse array items
.on('data', (item) => results.push(item))
.on('end', () => resolve(results))
.on('error', reject);
});
}
// PROBLEM: Synchronous crypto in request path
// const hash = crypto.pbkdf2Sync(password, salt, 100000, 64, 'sha512');
// SOLUTION: Use async variant
const crypto = require('crypto');
async function hashPassword(password, salt) {
return new Promise((resolve, reject) => {
crypto.pbkdf2(password, salt, 100000, 64, 'sha512', (err, key) => {
if (err) reject(err);
else resolve(key.toString('hex'));
});
});
}Asenkron pbkdf2 varyantı, CPU işini libuv iş parçacığı havuzuna aktararak event loop'u diğer istekleri işlemek için serbest bırakır. Bu tek değişiklik, belgelenmiş bir fintech üretim olayında p99 gecikmeyi 900ms'den 120ms'ye düşürmüştür.
Cluster Modülü ile Node.js'i CPU Çekirdekleri Arasında Ölçekleme
Tek bir Node.js işlemi bir CPU çekirdeği kullanır. 16 çekirdekli bir üretim sunucusunda, mevcut hesaplama gücünün %93'ü boşta kalır. Yerleşik cluster modülü, tek bir portu paylaşan worker işlemleri oluşturarak gelen bağlantıları tüm çekirdeklere dağıtır.
// Production clustering with graceful shutdown
const cluster = require('cluster');
const os = require('os');
const process = require('process');
const WORKER_COUNT = parseInt(process.env.WORKERS) || os.cpus().length;
if (cluster.isPrimary) {
console.log(`Primary ${process.pid} starting ${WORKER_COUNT} workers`);
// Fork workers for each CPU core
for (let i = 0; i < WORKER_COUNT; i++) {
cluster.fork();
}
// Restart crashed workers automatically
cluster.on('exit', (worker, code, signal) => {
if (!worker.exitedAfterDisconnect) {
console.error(`Worker ${worker.process.pid} died (${signal || code}). Restarting...`);
cluster.fork();
}
});
// Graceful shutdown on SIGTERM
process.on('SIGTERM', () => {
console.log('Primary received SIGTERM. Shutting down workers...');
for (const id in cluster.workers) {
cluster.workers[id].disconnect();
}
});
} else {
// Worker process — start the actual HTTP server
const http = require('http');
const server = http.createServer((req, res) => {
res.writeHead(200);
res.end(`Handled by worker ${process.pid}\n`);
});
server.listen(3000, () => {
console.log(`Worker ${process.pid} listening on port 3000`);
});
// Graceful shutdown for individual worker
process.on('SIGTERM', () => {
server.close(() => process.exit(0));
});
}Her worker, ayrı belleğe sahip kendi V8 izolasyonunda çalışır. Worker'lar durumu paylaşmaz — oturum verileri, önbellekler ve uygulama durumu Redis veya PostgreSQL gibi harici bir depoda tutulmalıdır. Bu izolasyon aynı zamanda hata toleransı da sağlar: bir worker'daki çökme diğerlerini etkilemez.
Node.js / NestJS mülakatlarında başarılı olmaya hazır mısın?
İnteraktif simülatörler, flashcards ve teknik testlerle pratik yap.
CPU Yoğun Görevler için Worker Thread'ler
cluster modülü tüm işlemi kopyalar. Node.js 10'da tanıtılan ve Node.js 12'den itibaren kararlı olan worker thread'ler, aynı işlem içinde paralel iş parçacıklarında JavaScript çalıştırır. SharedArrayBuffer aracılığıyla bellek paylaşır ve yapısal klonlama yoluyla veri aktarır.
Bu ayrım önemlidir: I/O'ya bağlı HTTP sunucularını çekirdekler arasında ölçeklemek için cluster'lar, CPU'ya bağlı işlemleri event loop'tan boşaltmak için worker thread'ler kullanılır.
// Reusable worker thread pool for CPU tasks
const { Worker } = require('worker_threads');
const os = require('os');
class WorkerPool {
constructor(workerScript, poolSize = os.cpus().length) {
this.workers = [];
this.queue = [];
for (let i = 0; i < poolSize; i++) {
this.workers.push({ busy: false, worker: new Worker(workerScript) });
}
}
execute(taskData) {
return new Promise((resolve, reject) => {
const available = this.workers.find(w => !w.busy);
if (available) {
this._runTask(available, taskData, resolve, reject);
} else {
// Queue task until a worker is free
this.queue.push({ taskData, resolve, reject });
}
});
}
_runTask(entry, taskData, resolve, reject) {
entry.busy = true;
entry.worker.postMessage(taskData);
const onMessage = (result) => {
entry.busy = false;
cleanup();
resolve(result);
this._processQueue();
};
const onError = (err) => {
entry.busy = false;
cleanup();
reject(err);
this._processQueue();
};
const cleanup = () => {
entry.worker.removeListener('message', onMessage);
entry.worker.removeListener('error', onError);
};
entry.worker.on('message', onMessage);
entry.worker.on('error', onError);
}
_processQueue() {
if (this.queue.length === 0) return;
const available = this.workers.find(w => !w.busy);
if (available) {
const { taskData, resolve, reject } = this.queue.shift();
this._runTask(available, taskData, resolve, reject);
}
}
}
module.exports = { WorkerPool };// Worker thread for CPU-intensive image processing
const { parentPort } = require('worker_threads');
const sharp = require('sharp');
parentPort.on('message', async ({ inputPath, width, height }) => {
const result = await sharp(inputPath)
.resize(width, height)
.webp({ quality: 80 })
.toBuffer();
parentPort.postMessage({ size: result.length, buffer: result });
});Worker havuzu, başlangıçta iş parçacıklarını önceden oluşturur ve istekler arasında yeniden kullanır. Bu, istek başına yeni bir worker thread oluşturmanın 30-50ms ek yükünü ortadan kaldırır. Görüntü işleme, PDF oluşturma veya veri dönüştürme için bu kalıp, sürekli CPU yükü altında bile ana event loop gecikmesini 5ms'nin altında tutar.
Bellek Optimizasyonu ve Çöp Toplayıcı Ayarlama
V8, heap'i genç nesil (kısa ömürlü nesneler) ve eski nesil (uzun ömürlü nesneler) olarak ayırır. Çoğu performans sorunu, sık küçük GC duraklamalarını tetikleyen genç nesildeki aşırı tahsislerden veya büyük GC duraklamalarının görünür gecikme artışlarına neden olana kadar eski nesli büyüten bellek sızıntılarından kaynaklanır.
// Patterns that reduce GC pressure
// ANTI-PATTERN: Creating objects in hot loops
function processItemsBad(items) {
return items.map(item => ({
id: item.id,
name: item.name.trim(),
score: calculateScore(item), // New object per iteration
metadata: { processed: true, timestamp: Date.now() }
}));
}
// OPTIMIZED: Reuse buffers and minimize allocations
const reusableBuffer = Buffer.alloc(4096);
function processItemsGood(items, output) {
// Reuse the output array instead of creating new one
output.length = 0;
for (let i = 0; i < items.length; i++) {
// Mutate in place when safe to do so
output.push(items[i].id);
}
return output;
}
// Monitor heap usage for leak detection
function checkMemory() {
const used = process.memoryUsage();
return {
heapUsedMB: Math.round(used.heapUsed / 1024 / 1024),
heapTotalMB: Math.round(used.heapTotal / 1024 / 1024),
externalMB: Math.round(used.external / 1024 / 1024),
rsssMB: Math.round(used.rss / 1024 / 1024),
};
}
// V8 flags for production GC tuning
// node --max-old-space-size=4096 --max-semi-space-size=128 app.js
// --max-old-space-size: Set old generation limit (default ~1.7GB)
// --max-semi-space-size: Increase young generation (default 16MB)--max-semi-space-size değerini varsayılan 16MB'den 64-128MB'ye yükseltmek, yüksek tahsis oranına sahip uygulamalar için küçük GC sıklığını azaltır. Bu, bellek ile daha düşük GC duraklaması sıklığı arasında bir takas yapar — 8GB+ RAM'e sahip sunucularda bu değerli bir değiş tokuştur.
Node.js 24'te OpenTelemetry ile Üretim İzleme
OpenTelemetry, 2026'da Node.js için standart enstrümantasyon çerçevesi haline gelmiştir. Node.js 24 çalışma zamanı, CPU profillerinde satır içi önbellek verileri ile geliştirilmiş profilleme desteği içerir ve bu da performans analizini önemli ölçüde daha doğru hale getirir.
// OpenTelemetry setup for Node.js performance monitoring
const { NodeSDK } = require('@opentelemetry/sdk-node');
const { getNodeAutoInstrumentations } = require('@opentelemetry/auto-instrumentations-node');
const { PrometheusExporter } = require('@opentelemetry/exporter-prometheus');
const { PeriodicExportingMetricReader } = require('@opentelemetry/sdk-metrics');
const sdk = new NodeSDK({
metricReader: new PrometheusExporter({ port: 9464 }),
instrumentations: [
getNodeAutoInstrumentations({
// Instrument HTTP, Express, DNS, fs, and more
'@opentelemetry/instrumentation-fs': { enabled: false }, // Too noisy
}),
],
});
sdk.start();
// Custom event loop lag metric
const { metrics } = require('@opentelemetry/api');
const meter = metrics.getMeter('app');
const eventLoopLag = meter.createHistogram('nodejs.event_loop.lag', {
description: 'Event loop lag in milliseconds',
unit: 'ms',
});
// Report event loop lag every second
const { monitorEventLoopDelay } = require('perf_hooks');
const h = monitorEventLoopDelay({ resolution: 10 });
h.enable();
setInterval(() => {
eventLoopLag.record(h.percentile(99) / 1e6);
h.reset();
}, 1000);Bu kurulum, event loop gecikmesini, HTTP istek süresini ve DNS çözümleme süresini Prometheus metrikleri olarak dışa aktarır. Event loop gecikmesi p99 > 100ms üzerinde uyarı ayarlamak, kullanıcılar fark etmeden bozulmayı yakalar.
Her cluster worker'ı kendi libuv iş parçacığı havuzunu alır (varsayılan 4 iş parçacığı). 16 cluster worker'lı 16 çekirdekli bir makinede bu, CPU zamanı için yarışan 64 libuv iş parçacığı anlamına gelir. Kümelenmiş ortamlarda UV_THREADPOOL_SIZE değerini worker başına 2-4 olarak ayarlayın ve CPU görevlerini işleyen worker thread'ler için çekirdek ayırın.
Cluster, Worker Thread ve Harici Ölçekleme Arasında Seçim
Doğru ölçekleme stratejisi iş yükü profiline bağlıdır. Gerçek üretim kalıplarına dayalı karar matrisi:
| Senaryo | Strateji | Neden | |----------|----------|--------| | HTTP API, çoğunlukla I/O | Cluster modülü (çekirdek başına 1 worker) | Bağlantı verimini maksimize eder | | Görüntü/video işleme | Worker thread havuzu (4-8 iş parçacığı) | Event loop'u duyarlı tutar | | CPU yoğun veri hattı | Worker threads + SharedArrayBuffer | Sıfır kopyalama ile veri paylaşımı | | Ölçekte mikro servisler | Kubernetes pod'ları (tek işlem konteynerleri) | Orkestratör ölçeklemeyi yönetir | | Karma I/O + CPU | Cluster + worker başına worker havuzu | Her worker CPU'yu thread'lere aktarır |
Kubernetes üzerinde konteynerize dağıtımlar için, konteyner başına tek bir Node.js işlemi çalıştırmak (kümeleme olmadan) genellikle daha basit ve öngörülebilirdir. Orkestratör yatay ölçeklemeyi, sağlık kontrollerini ve yuvarlamalı yeniden başlatmaları yönetir. Kümeleme, tek bir makinenin çekirdek kullanımını maksimize etmesi gereken bare metal veya sanal makinelerde değer katar.
Node.js 24, 2025'te piyasaya sürülmüş olup V8 12.4 ile API iş yüklerinde %8-12 daha iyi verim sağlar. Yerleşik fetch() artık varsayılan olarak HTTP/2 ve HTTP/3 desteğiyle Undici 7.0 kullanır. İzin modeli ayrıca derinlemesine savunma için worker başına dosya sistemi ve ağ erişimini kilitlemeye olanak tanır.
Üretim için Pratik Optimizasyon Kontrol Listesi
Bu tekniklerin etki sırasına göre uygulanması — önce event loop sağlığı, ardından ölçekleme, sonra ince ayar — minimum riskle ölçülebilir iyileştirmeler sağlar.
- Event loop kullanımını sürekli izleme —
performance.eventLoopUtilization()ile %70 eşiğinde uyarı - Optimize etmeden önce profilleme — gerçek darboğazları belirlemek için
node --profveya OpenTelemetry izleri kullanılmalı, varsayılan değil - Senkron işlemleri ana iş parçacığından uzaklaştırma —
crypto.pbkdf2, büyük yüklerdeJSON.parse, görüntü işleme hepsi worker thread'lere ait - İş yüküne göre
UV_THREADPOOL_SIZEayarlama — varsayılan 4, yoğun DNS veya dosya I/O olan uygulamalar için çok düşük, kümelenmiş kurulumlarda çok yüksek - Büyük veriler için akış kullanma —
JSONStream,csv-parserve Node.jsstream.pipeline()büyük yüklerde bellek artışlarını önler - Başlangıçta worker havuzlarını ısıtma — CPU yoğun uç noktalara ilk isteklerde soğuk başlangıç gecikmesini önleme
- V8 heap bayraklarını dağıtım için ayarlama — mevcut bellek ve tahsis kalıplarına göre
--max-old-space-sizeve--max-semi-space-size - Zarif kapatma uygulama — SIGTERM'de bağlantıları boşaltma, veritabanı havuzlarını kapatma ve işlem çıkışından önce uçuştaki isteklerin tamamlanmasına izin verme
Pratik yapmaya başla!
Mülakat simülatörleri ve teknik testlerle bilgini test et.
Sonuç
Node.js performansı 2026'da üç temel üzerine odaklanır:
- Event loop, verim için tek hata noktasıdır — kullanım izleme ve bloklayıcıları ortadan kaldırma gecikme üzerinde en yüksek etkiye sahiptir
- Kümeleme, I/O'ya bağlı iş yüklerini çekirdekler arasında ölçekler; worker thread'ler CPU'ya bağlı görevleri event loop'tan boşaltır — her ikisini birlikte kullanmak maksimum donanım kullanımını sağlar
- OpenTelemetry ve V8 profilleme araçları ile üretim izleme, tahminleri veriye dönüştürür — event loop gecikme p99 ve heap büyüme hızı üzerinde uyarılar ayarlanmalıdır
- Node.js 24, V8 12.4 performans kazanımları, Undici 7 ile kararlı HTTP/3 ve olgun bir izin modeli getirir — Node.js 20 veya öncesinden yükseltme ölçülebilir verim iyileştirmeleri sağlar
- Herhangi bir mevcut Node.js uygulamasında
performance.eventLoopUtilization()ile başlamak — sonuçlar genellikle mevcut en yüksek etkili tek optimizasyonu ortaya koyar
Daha fazla Node.js ve NestJS mülakat hazırlığı için Node.js & NestJS teknoloji yolunu ve üretim mimarisi kalıplarının derinlemesine ele alındığı middleware ve interceptor modülünü keşfetmek faydalı olacaktır. Node.js backend mülakat soruları rehberi kıdemli mühendislik mülakatlarında sıkça sorulan ek ileri düzey konuları kapsar.
Etiketler
Paylaş
İlgili makaleler

Node.js Backend Mülakat Soruları: Kapsamlı Rehber 2026
Node.js backend mülakatlarında en sık sorulan 25 soru. Event loop, async/await, stream, kümeleme ve performans konuları detaylı cevaplarla açıklanmaktadır.

NestJS: Eksiksiz bir REST API Oluşturma
NestJS ile profesyonel bir REST API oluşturmak için kapsamlı rehber. Controller, Service, Module yapıları, class-validator ile doğrulama ve hata yönetimi pratik örneklerle açıklanmaktadır.