Yazılarımız

OfisData

NODE.JS STREAM KULLANMAK VE BELLEK TÜKETİMİNİ AZALTMAK

Büyük dosyaları okumak, log akışlarını işlemek veya bir API’den gelen veriyi anlık dönüştürmek… Node.js tarafında bu senaryoların ortak bir riski vardır: belleği gereksiz şişirmek. Veriyi “tamamını al, sonra işle” yaklaşımıyla ele aldığınızda, trafik veya dosya boyutu büyüdükçe süreç hem yavaşlar hem de çökme ihtimali artar.

İşte bu noktada Node.js stream yaklaşımı devreye girer. Stream, veriyi parça parça (chunk) işler; böylece daha az bellek tüketir, gecikmeyi düşürür ve sistemin stabil kalmasına yardımcı olur. Üstelik doğru tasarlandığında, yoğun yükte bile kontrollü bir performans sunar.

Bu yazıda stream türlerini, backpressure mantığını, pipeline kullanımını, hata yakalamayı ve gerçekçi örneklerle bellek tüketimini azaltmayı ele alacağız. Konuyu daha derin sistematik ilerletmek için nodejs eğitimi içeriğine de bakabilirsiniz.

Geliştirici ekranında Node.js akış diyagramı, dosya okuma ve yazma adımları ile bellek grafiği bulunan çalışma masası

Stream yaklaşımını temel olarak kavramak

Stream, veriyi tek seferde RAM’e yüklemek yerine küçük parçalar halinde okuyup işler. Bu sayede büyük dosyalar ve uzun süreli veri akışları daha güvenli yönetilir. Stream ile çalışırken hedef; akış bazlı işleme kurmak, gereksiz kopyalamayı azaltmak ve gecikmeyi düşürmektir.

Chunk bazlı okumayı anlamak ve belleği korumak

Bir dosyayı fs.readFile ile okumak, dosyanın tamamını belleğe alır. Stream ile okuduğunuzda ise dosya parça parça gelir. Bu yaklaşım özellikle 100MB+ dosyalarda belirgin fark yaratır.

Akış türlerini bilmek ve doğru yerde kullanmak

Node.js ekosisteminde dört temel stream türü vardır: Readable, Writable, Duplex ve Transform. Her biri farklı senaryolarda ölçeklenebilir iş akışları kurmaya yardımcı olur.


Readable ve Writable akışını doğru bağlamak

Dosya kopyalama gibi klasik senaryolarda Readable ile okur, Writable ile yazarsınız. Buradaki kritik nokta, veriyi “buffer biriktirerek” değil, akış halinde aktarmaktır.

Dosya kopyalamayı stream ile yapmak ve RAM tüketimini azaltmak

const fs = require("fs");

const readStream = fs.createReadStream("big-file.log");
const writeStream = fs.createWriteStream("big-file-copy.log");

readStream.on("error", (err) => console.error("read error:", err));
writeStream.on("error", (err) => console.error("write error:", err));

readStream.pipe(writeStream);

Bu yöntemle, dosyanın tamamı belleğe alınmaz. Sistem, veriyi küçük parçalar halinde yazıya aktarır. Yük altında stabil kalmak için en pratik adımlardan biridir.

HighWaterMark ayarlamayı düşünmek ve akışı optimize etmek

Bazı durumlarda stream’in iç buffer boyutunu ayarlamak faydalı olabilir. Çok küçük değerler CPU kullanımını artırabilir, çok büyük değerler ise bellek tüketimini yükseltebilir. Burada hedef, iş yüküne göre dengeli bir konfigürasyon seçmektir.


Backpressure yönetimini anlamak ve tıkanmayı önlemek

Backpressure, yazma tarafı (Writable) yavaşladığında okuma tarafının (Readable) frenlenmesi ihtiyacıdır. Aksi halde, okunan veri yazılamayıp buffer’da birikir ve bellek şişer. Stream’in gerçek gücü, bu akış kontrolünü doğal şekilde yönetebilmesidir.

Pipe kullanarak akış kontrolünü devretmek ve güvenli ilerlemek

pipe() çoğu senaryoda backpressure yönetimini otomatik yapar. Bu nedenle manuel okuma-yazma döngüsü kurmak yerine pipe veya pipeline tercih etmek daha güvenlidir.

Drain event ile manuel yazmayı yönetmek ve taşmayı azaltmak

Manuel yazma yapıyorsanız, write() geriye false dönebilir. Bu, buffer doldu demektir; drain gelene kadar okumayı durdurmak gerekir.

const fs = require("fs");

const rs = fs.createReadStream("big-file.csv");
const ws = fs.createWriteStream("out.csv");

rs.on("data", (chunk) => {
  const canContinue = ws.write(chunk);
  if (!canContinue) {
    rs.pause();
    ws.once("drain", () => rs.resume());
  }
});

rs.on("end", () => ws.end());

Bu yaklaşım özellikle Transform işlemleriyle birleştirildiğinde, kontrolün sizde olduğu ama taşmanın önlendiği bir yapı sağlar.


Transform stream kullanmak ve veriyi akış halinde dönüştürmek

Transform stream, gelen veriyi dönüştürüp sonraki adıma aktarır. Log maskeleme, CSV satır temizleme veya JSON satır zenginleştirme gibi işlerde çok verimli çalışır. Burada amaç, veriyi önce biriktirmeden işlemektir.

Satır bazlı dönüştürmeyi kurgulamak ve iş akışını sadeleştirmek

Transform ile parça parça gelen veriyi temizleyip yazabilirsiniz. En kritik nokta, dönüştürmenin idempotent ve hata toleranslı tasarlanmasıdır.

Pipeline ile zinciri kurmak ve hata yakalamayı kolaylaştırmak

stream.pipeline (veya pipeline helper) ile akışları birbirine bağlamak, hataları tek noktadan yönetmeyi kolaylaştırır. Böylece kopan bir zincirde kaynaklar daha güvenli kapatılır.


HTTP ve dosya akışını birlikte yönetmek

Stream yaklaşımı yalnızca dosya için değil, HTTP response için de kritik bir avantaj sağlar. Büyük bir dosyayı API üzerinden indirtirken veya dış servislerden veri çekerken, veriyi anlık aktarmak bellek tüketimini ciddi biçimde düşürür.

Response üzerinden stream etmek ve büyük payload taşımayı azaltmak

Özellikle rapor indirme, medya aktarımı veya proxy senaryolarında response’a stream etmek, bekleme süresini azaltır ve kullanıcıya daha hızlı geri bildirim verir.

Timeout ve abort yönetmek ve yarım kalan işleri temizlemek

Uzun süren akışlarda bağlantı kopabilir. Abort sinyallerini yakalayıp stream’i kapatmak, sistem kaynaklarını boşa harcamamak için kritiktir. Hata ayıklamayı kolaylaştırmak için de log standartlarıyla birlikte düşünmek gerekir.


Stream ile performans standardı kurmak ve ölçmek

Akış bazlı bir yaklaşım kurguladıktan sonra, performansı ölçmek ve standartlaştırmak gerekir. “Hızlandı” hissi yerine metriklerle ilerlemek daha güvenilir sonuç verir.

  • Büyük dosya işlerinde bellek kullanımını takip etmek
  • Akış zincirinde hata oranını izlemek
  • Backpressure oluştuğu noktaları tespit etmek
  • Pipeline ile kaynakları doğru kapatmak
  • Gereksiz buffer kopyalamayı azaltmak

Özetle, Node.js stream kullanmak büyük veri ve dosya senaryolarında hem performans hem de stabilite açısından güçlü bir yaklaşımdır. Doğru stream türünü seçmek, backpressure yönetmek, pipeline ile zinciri sağlam kurmak ve ölçüm odaklı ilerlemek; bellek tüketimini azaltıp daha güvenilir servisler geliştirmeyi sağlar.

 Vimaj