Ana içeriğe geç

🎯 Giriş

Node.js tek iş parçacıklı bir mimariye sahiptir. Bu yapı, I/O odaklı işlemler için mükemmel olsa da CPU yoğun görevler Event Loop’u bloke eder ve tüm uygulamanın yanıt verme süresini düşürür.

Bu rehberde, Node.js’in worker_threads yapısını kullanarak CPU yoğun işleri ana iş parçacığından ayırmayı, iş yüklerini gerçek paralellik ile çalıştırmayı ve üretimde kullanılan işçi havuzlarını, kaynak limitlerini ve güvenlik uygulamalarını öğreneceksiniz.


📘 Bu Rehberde Ne Öğreneceksiniz?

  • worker_threads modülünün mantığı
  • CPU yoğun görevleri ana iş parçacığından ayırma
  • Tekil worker kullanımı
  • Piscina ile Worker Pool kurma
  • Kaynak limitleri: bellek, timeout, stack
  • Görev kuyruğu & performans izleme (Prometheus)
  • Üretim ortamında güvenli multithreading

1. Node.js Çoklu İş Parçacığı Mantığını Anlamak

Node.js'te iki çalışma modeli vardır:

YapıAçıklama
Event LoopI/O işlerini yönetir. Tek iş parçacıklıdır.
Worker ThreadsCPU yoğun işleri paralel yürütmek için ek iş parçacıklarıdır.

Worker Thread’ler şunları sağlar:

  • Ana iş parçacığından bağımsız çalışma
  • Kendi Event Loop’larına sahip olma
  • postMessage ile veri aktarımı
  • SharedArrayBuffer ile paylaşımlı bellek

✔ Bu sayede CPU yoğun işler sunucuyu dondurmaz.


2. Proje Kurulumu

Dizin oluşturma

mkdir rabisu-worker-demo
cd rabisu-worker-demo
npm init -y

ES Modules aktif etme


npm pkg set type=module

Gerekli paketler


npm install express piscina poolifier p-queue prom-client

3. İşçi (Worker) Dosyası Oluşturma


// worker.js
import { parentPort, workerData } from 'node:worker_threads';
import { createHash } from 'node:crypto';

function hashBuffer(payload) {
const hash = createHash('sha256');
hash.update(payload, 'utf8');
return hash.digest('hex');
}

try {
const result = hashBuffer(workerData.payload);
parentPort.postMessage({ status: 'ok', result });
} catch (error) {
parentPort.postMessage({ status: 'error', message: error.message });
}
Bu worker, CPU yoğun SHA-256 hash işlemi yapar.
Sonucu ana iş parçacığına gönderir.

4. Tek İşçi ile Görev Çalıştırma (Klasik Yöntem)


// index.js
import express from 'express';
import { Worker } from 'node:worker_threads';
import { join, dirname } from 'node:path';
import { fileURLToPath } from 'node:url';

const __filename = fileURLToPath(import.meta.url);
const __dirname = dirname(__filename);
const workerPath = join(__dirname, 'worker.js');

const app = express();
app.use(express.json({ limit: '1mb' }));

function runWorker(workerData) {
return new Promise((resolve, reject) => {
const worker = new Worker(workerPath, {
workerData,
resourceLimits: { maxOldGenerationSizeMb: 64, stackSizeMb: 4 }
});

const timeout = setTimeout(() => {
worker.terminate();
reject(new Error('İşçi zaman aşımı (10s)'));
}, 10_000);

worker.once('message', msg => {
clearTimeout(timeout);
worker.terminate();
msg.status === 'ok' ? resolve(msg.result) : reject(msg.message);
});

worker.once('error', err => reject(err));
});
}

app.post('/api/hash-tekil', async (req, res) => {
const hash = await runWorker({ payload: req.body.text });
res.json({ hash });
});

app.listen(3000, () => console.log('Sunucu çalıştı')); ✔ Her istek için yeni worker oluşturulur → maliyetlidir. ✔ Yüksek trafiğe GELMEZ.

Bu yüzden Worker Pool gerekir.


5. Worker Pool (Piscina) ile Yüksek Performans


import Piscina from 'piscina';
import { resolve, dirname } from 'node:path';
import { fileURLToPath } from 'node:url';

const __filename = fileURLToPath(import.meta.url);
const __dirname = dirname(__filename);

export const piscina = new Piscina({
filename: resolve(__dirname, 'worker.js'),
minThreads: 2,
maxThreads: Math.max(4, Piscina.availableParallelism()),
idleTimeout: 30000,
resourceLimits: { maxOldGenerationSizeMb: 80 }
});

export async function hashWithPool(payload) {
const result = await piscina.run({ payload });
return result.result;
}

✔ İşçiler oluşturulup saklanır ✔ İşler boş işçilere dağıtılır ✔ Queue sistemi performansı artırır


6. Pool’u API’ye entegre etme


import express from 'express';
import { hashWithPool } from './pool.js';

const app = express();
app.use(express.json());

app.post('/api/pool/hash', async (req, res) => {
const hash = await hashWithPool(req.body.text);
res.json({ hash });
});

app.listen(3000, () => console.log('Pool aktif'));

7. Kaynak Limitleri ve Güvenlik

Worker oluştururken:


resourceLimits: {
maxOldGenerationSizeMb: 64,
maxYoungGenerationSizeMb: 16,
stackSizeMb: 4
}

✔ Bellek taşmasını engeller ✔ Sonsuz döngü riskini azaltır ✔ Sunucunun çökmesini önler

Payload doğrulaması:


if (req.body.text.length > 1_000_000) {
return res.status(400).json({ error: 'Yük çok büyük.' });
}

8. Üretim Ortamı İzleme (Prometheus)


import client from 'prom-client';

export function createMetricsRegistry(piscina) {
const register = new client.Registry();
client.collectDefaultMetrics({ register });

const queueGauge = new client.Gauge({
name: 'worker_queue_size',
help: 'Kuyrukta bekleyen iş sayısı'
});

register.registerMetric(queueGauge);

setInterval(() => {
queueGauge.set(piscina.queueSize);
}, 5000).unref();

return register;
}

9. /metrics endpoint


import { createMetricsRegistry } from './metrics.js';
import { piscina } from './pool.js';

const register = createMetricsRegistry(piscina);

app.get('/metrics', async (req, res) => {
res.set('Content-Type', register.contentType);
res.end(await register.metrics());
});

✔ Grafana & Prometheus ile tam izleme yapılabilir. ✔ Worker kuyruk yoğunluğu takip edilir.


10. SSS – Sıkça Sorulan Sorular

  1. Node.js neden CPU yoğun işleri kaldıramıyor?

Event Loop tek iş parçacıklıdır → CPU yoğun iş tüm sistemi dondurur.

  1. Worker Thread’ler neden daha iyi?

Ayrı bir event loop ve ayrı bir JS bağlamına sahiptir.

  1. Worker Pool kullanmak zorunlu mu?

Trafik yoğunsa EVET.

  1. cluster ile worker_threads farkı?

cluster → process bazlı

worker_threads → thread bazlı


✔ Sonuç

Bu rehberle:

CPU yoğun işlemleri ana iş parçacığından ayırdın

Worker Thread’leri öğrendin

Piscina ile yüksek performanslı havuz kurdun

Kaynak limiti ve güvenlik tekniklerini uyguladın

Prometheus ile üretim takibi ekledin

Node.js uygulaman artık gerçek paralelliğe geçti. Bu mimariyi Rabisu Bulut üzerinde çok çekirdekli sunucularda deneyerek maksimum performans elde edebilirsin. 🚀🔥