Merhaba arkadaşlar, bugün başımı çok ağrıtan bir sorunu ve nasıl çözdüğümü anlatacağım. Bir projede, kullanıcıların yüklediği GB'larca CSV ve log dosyalarını işlemem gerekiyordu. İlk başta klasik `fs.readFile` ile okuduğumda, sunucu belleği kısa sürede tükeniyor ve uygulama çöküyordu. Bu hatayı ilk gördüğümde kafayı yemiştim, çünkü dosya boyutu arttıkça memory kullanımı da doğru orantılı artıyordu.
İşte o zaman Node.js'in güçlü yanlarından biri olan Stream'lerin nimetlerinden faydalanmaya karar verdim. Stream'ler, veriyi küçük parçalar (chunk'lar) halinde okuyup işleyerek, tüm dosyayı bir kerede belleğe yüklemekten kurtarıyor. Bu sayede 10GB'lık bir dosyayı işlerken bile memory kullanımınız birkaç MB'ı geçmiyor.
Karşılaştığım Sorun ve Naif Çözüm
İlk denememdeki kod buydu. Küçük dosyalar için çalışsa da, büyükler için felaketti:
Bu yöntemde, `data` değişkeni tüm dosya içeriğini tuttuğu için, dosya ne kadar büyükse o kadar çok RAM tüketiyordu.
Stream'lerle Gelen Kurtuluş
Çözüm, veriyi bir nehir gibi akıtmakta yatıyor. `fs.createReadStream` ile bir okuma stream'i oluşturup, `readline` modülü ile satır satır işliyoruz.
Bu yapıda, dosya tek seferde değil, küçük parçalar halinde okunuyor. `readline` modülü, bu parçaları satır sonlarına (`\n`) göre ayırıp bize tek tek `line` eventi olarak sunuyor. Bellekte sadece o an işlenen satır bulunuyor.
İleri Seviye Optimizasyon: Pipeline ve Transform Stream
Eğer veriyi okurken aynı anda dönüştürmeniz gerekiyorsa (örn: CSV'den JSON'a, veri temizleme), Node.js'in dahili `stream.Transform` sınıfı mükemmel bir seçenek. İşte bir örnek:
`pipeline` fonksiyonu, stream'leri birbirine bağlar ve hata yönetimini merkezileştirir. Bu, `.pipe()` kullanmaktan daha güvenli ve modern bir yöntem.
Sonuç olarak, büyük veri işlemlerinde stream kullanmak bir lüks değil, bir zorunluluk. Bu yöntemle uygulamamın memory kullanımı %95 oranında azaldı ve GB'larca dosyayı sorunsuz işleyebilir hale geldim.
Siz Node.js projelerinizde büyük dosyaları nasıl işliyorsunuz? `readline` dışında tercih ettiğiniz stream kütüphaneleri var mı? Yaşadığınız ilginç performans sorunlarını ve çözümlerinizi yorumlarda paylaşın, tartışalım!
İşte o zaman Node.js'in güçlü yanlarından biri olan Stream'lerin nimetlerinden faydalanmaya karar verdim. Stream'ler, veriyi küçük parçalar (chunk'lar) halinde okuyup işleyerek, tüm dosyayı bir kerede belleğe yüklemekten kurtarıyor. Bu sayede 10GB'lık bir dosyayı işlerken bile memory kullanımınız birkaç MB'ı geçmiyor.
İlk denememdeki kod buydu. Küçük dosyalar için çalışsa da, büyükler için felaketti:
JavaScript:
const fs = require('fs');
const processData = (filePath) => {
// TÜM DOSYA BELLEĞE YÜKLENİYOR!
fs.readFile(filePath, 'utf8', (err, data) => {
if (err) throw err;
const lines = data.split('\n');
lines.forEach(line => {
// Her satırı işle...
console.log(line);
});
});
};
Bu yöntemde, `data` değişkeni tüm dosya içeriğini tuttuğu için, dosya ne kadar büyükse o kadar çok RAM tüketiyordu.
Çözüm, veriyi bir nehir gibi akıtmakta yatıyor. `fs.createReadStream` ile bir okuma stream'i oluşturup, `readline` modülü ile satır satır işliyoruz.
JavaScript:
const fs = require('fs');
const readline = require('readline');
const processLargeFile = (filePath) => {
return new Promise((resolve, reject) => {
const fileStream = fs.createReadStream(filePath, { encoding: 'utf8' });
// Readline interface'i oluştur
const rl = readline.createInterface({
input: fileStream,
crlfDelay: Infinity // Tüm CRLF (\r\n) ve LF (\n) satır sonlarını düzgün yakala
});
let lineCount = 0;
// 'line' eventi, her yeni satır okunduğunda tetiklenir
rl.on('line', (line) => {
lineCount++;
// Burada satırı işliyoruz. Sadece o anki satır bellekte.
// Örnek: CSV parsing, log analizi, veri filtreme
// console.log(`Satır ${lineCount}: ${line.substring(0, 50)}...`);
// ÖNEMLİ: Bu callback içinde ağır senkron işlemler YAPMA.
// Ağır işlemler için worker thread veya queue düşün.
});
// Stream bittiğinde
rl.on('close', () => {
console.log(`Dosya işlendi. Toplam satır: ${lineCount}`);
resolve(lineCount);
});
// Hata durumunda
fileStream.on('error', (err) => {
reject(err);
});
});
};
// Kullanımı
(async () => {
try {
await processLargeFile('devasa_log_file.log');
} catch (error) {
console.error('Dosya işlenirken hata:', error);
}
})();
Bu yapıda, dosya tek seferde değil, küçük parçalar halinde okunuyor. `readline` modülü, bu parçaları satır sonlarına (`\n`) göre ayırıp bize tek tek `line` eventi olarak sunuyor. Bellekte sadece o an işlenen satır bulunuyor.
Eğer veriyi okurken aynı anda dönüştürmeniz gerekiyorsa (örn: CSV'den JSON'a, veri temizleme), Node.js'in dahili `stream.Transform` sınıfı mükemmel bir seçenek. İşte bir örnek:
JavaScript:
const { Transform, pipeline } = require('stream');
const fs = require('fs');
const csv = require('csv-parser'); // Örnek bir parser
// Özel bir Transform Stream oluşturalım
const jsonTransformer = new Transform({
objectMode: true, // JavaScript objeleri ile çalışıyoruz
transform(chunk, encoding, callback) {
// 'chunk' burada csv-parser'dan gelen bir satır objesi
// Veriyi dönüştür
const transformedData = {
id: chunk.id,
name: chunk.name.toUpperCase(),
timestamp: new Date()
};
// Dönüştürülmüş veriyi bir sonraki adıma ilet
this.push(JSON.stringify(transformedData) + '\n');
callback();
}
});
// Pipeline ile akışı birleştir
pipeline(
fs.createReadStream('large_data.csv'),
csv(), // CSV'yi parse eden stream
jsonTransformer, // Bizim dönüştürücümüz
fs.createWriteStream('output.jsonl'),
(err) => {
if (err) {
console.error('Pipeline hatası:', err);
} else {
console.log('Dönüştürme tamamlandı!');
}
}
);
`pipeline` fonksiyonu, stream'leri birbirine bağlar ve hata yönetimini merkezileştirir. Bu, `.pipe()` kullanmaktan daha güvenli ve modern bir yöntem.
Sonuç olarak, büyük veri işlemlerinde stream kullanmak bir lüks değil, bir zorunluluk. Bu yöntemle uygulamamın memory kullanımı %95 oranında azaldı ve GB'larca dosyayı sorunsuz işleyebilir hale geldim.
Siz Node.js projelerinizde büyük dosyaları nasıl işliyorsunuz? `readline` dışında tercih ettiğiniz stream kütüphaneleri var mı? Yaşadığınız ilginç performans sorunlarını ve çözümlerinizi yorumlarda paylaşın, tartışalım!