berkmenoo 1
berkmenoo
mannaxxx 1
mannaxxx
Agora Metin2 1
Agora Metin2
[DEV]AB 1
[DEV]AB
kaptanmikro1 1
kaptanmikro1
Hikaye Ekle

[NODE JS] child_process.spawn ile Process Başlatma

  • Konuyu başlatan Konuyu başlatan Silverhand
  • Başlangıç tarihi Başlangıç tarihi
  • Cevaplar Cevaplar 1
  • Görüntüleme Görüntüleme 13

Türkiye'de ilk Mobil & PC Aynı Anda Metin2 Oyna. Triarchonline kalıcı ve uzun ömürlü yapısı ile 24 Temmuz'da açılıyor. | 1-99 Mobil Metin2 Triarch HEMEN TIKLA!

nodejs.png


[NODE JS] child_process.spawn ile Process Başlatma​


Bir önceki konuda exec metodunu inceledik. Şimdi ise child_process modülünün en güçlü ve esnek metodu olan spawn'a geçiyoruz. Spawn, exec'ten farklı olarak çıktıyı stream olarak döndürür, bu da onu büyük miktarda veri üreten uzun süren işlemler için ideal kılar. Ben bir video işleme platformunda FFmpeg komutlarını Node.js'den çalıştırmam gerektiğinde spawn'ın gerçek gücünü keşfettim. Saatlerce süren dönüştürme işlemlerinin ilerleme bilgisini gerçek zamanlı olarak takip edebilmek müthiş bir avantajdı.

Spawn ile exec arasındaki temel fark, veri akışı yaklaşımıdır. Exec tüm çıktıyı bellekte biriktirir ve işlem tamamlandığında döndürür. Spawn ise stdout ve stderr'i stream olarak sunar, bu sayede çıktıyı parça parça işleyebilirsiniz. Bu fark, büyük çıktı üreten komutlar için kritik öneme sahiptir. Örneğin bir log dosyasını tail -f ile izlerken veya sürekli veri üreten bir sensörü okurken spawn kullanmanız gerekir.

Spawn'ın bir diğer önemli avantajı, varsayılan olarak shell kullanmamasıdır. Komutu doğrudan çalıştırır, bu da komut enjeksiyonu riskini önemli ölçüde azaltır. Shell özelliklerine ihtiyaç duyduğunuzda shell: true seçeneğini açıkça belirtmeniz gerekir ama mümkünse bunu yapmaktan kaçınmalısınız.

Uzun süren işlemlerde sinyal yönetimi önemli bir konudur. Spawn ile başlatılan bir prosese SIGTERM, SIGINT veya SIGKILL sinyalleri gönderebilirsiniz. SIGTERM, prosese nazikçe kapanmasını söyler; proses bu sinyali yakalayıp temizlik işlemleri yapabilir. SIGKILL ise prosesi anında öldürür ve yakalanamaz. Üretim ortamlarında önce SIGTERM gönderip bir süre beklemek, yanıt alamazsanız SIGKILL göndermek iyi bir pratiktir.

Stdio konfigürasyonu, spawn'ın en esnek özelliklerinden biridir. stdin, stdout ve stderr akışlarını pipe, inherit, ignore veya bir dosya tanımlayıcısı olarak yapılandırabilirsiniz. Pipe modunda akışlara programatik olarak erişirsiniz. Inherit modunda çocuk prosesin akışları ana prosesinkilere bağlanır. Ignore modunda akışlar yok sayılır. Bu esneklik, çok çeşitli senaryolara uyum sağlamanızı mümkün kılar.

Proses yönetimi de spawn ile gelen önemli bir yetenektir. Başlatılan prosesin PID'sini alabilir, çıkış kodunu izleyebilir, CPU ve bellek kullanımını takip edebilirsiniz. Ayrıca detached seçeneğiyle çocuk prosesi ana prosesten bağımsız hale getirebilirsiniz. Bu, daemon (arka plan servisi) oluşturmak için kullanışlıdır; ana proses kapansa bile çocuk proses çalışmaya devam eder.

Pipe mekanizmasıyla birden fazla prosesi zincirleyebilirsiniz, tıpkı shell'deki pipe operatörü gibi. Bir prosesin stdout'unu diğerinin stdin'ine bağlayarak veri işleme pipeline'ları oluşturabilirsiniz. Bu yaklaşım Unix felsefesiyle tam uyumludur: her araç tek bir işi iyi yapar ve araçlar birbirine bağlanarak karmaşık görevleri gerçekleştirir.

JavaScript:
const { spawn, execFile } = require('child_process');
const path = require('path');
const fs = require('fs');

// ===== TEMEL SPAWN KULLANIMI =====

// Basit komut çalıştırma
function basitKomut() {
  const proses = spawn('node', ['--version']);

  proses.stdout.on('data', (veri) => {
    console.log('Node.js sürümü:', veri.toString().trim());
  });

  proses.stderr.on('data', (veri) => {
    console.error('Hata:', veri.toString());
  });

  proses.on('close', (cikisKodu) => {
    console.log('Proses tamamlandı, çıkış kodu:', cikisKodu);
  });

  proses.on('error', (hata) => {
    console.error('Proses başlatılamadı:', hata.message);
  });
}

// ===== GELİŞMİŞ PROSES YÖNETİMİ =====

class ProsesYoneticisi {
  constructor() {
    this.aktifProsesler = new Map();
    this.tamamlananlar = [];
  }

  // Yeni proses başlat
  baslat(isim, komut, argumanlar, secenekler = {}) {
    return new Promise((resolve, reject) => {
      const prosesSecenekleri = {
        cwd: secenekler.cwd || process.cwd(),
        env: { ...process.env, ...secenekler.env },
        stdio: ['pipe', 'pipe', 'pipe'],
        shell: secenekler.shell || false,
        timeout: secenekler.timeout || 0
      };

      console.log('Proses başlatılıyor: ' + isim + ' (' + komut + ' ' + argumanlar.join(' ') + ')');
      const baslangicZamani = Date.now();

      const cocukProses = spawn(komut, argumanlar, prosesSecenekleri);
      let stdCikti = '';
      let stdHata = '';

      this.aktifProsesler.set(isim, {
        proses: cocukProses,
        pid: cocukProses.pid,
        baslangic: baslangicZamani
      });

      console.log('Proses ID:', cocukProses.pid);

      // Stdout stream işleme
      cocukProses.stdout.on('data', (parca) => {
        const metin = parca.toString();
        stdCikti += metin;

        if (secenekler.canliCikti) {
          process.stdout.write('[' + isim + '] ' + metin);
        }
      });

      // Stderr stream işleme
      cocukProses.stderr.on('data', (parca) => {
        const metin = parca.toString();
        stdHata += metin;

        if (secenekler.canliCikti) {
          process.stderr.write('[' + isim + ' HATA] ' + metin);
        }
      });

      // Proses tamamlandığında
      cocukProses.on('close', (cikisKodu, sinyal) => {
        const sure = Date.now() - baslangicZamani;
        this.aktifProsesler.delete(isim);

        const sonuc = {
          isim: isim,
          pid: cocukProses.pid,
          cikisKodu: cikisKodu,
          sinyal: sinyal,
          sure: sure,
          stdCikti: stdCikti,
          stdHata: stdHata
        };

        this.tamamlananlar.push(sonuc);

        if (cikisKodu === 0) {
          console.log('✓ ' + isim + ' tamamlandı (' + sure + 'ms)');
          resolve(sonuc);
        } else {
          console.error('✗ ' + isim + ' başarısız (kod: ' + cikisKodu + ', süre: ' + sure + 'ms)');
          reject(sonuc);
        }
      });

      cocukProses.on('error', (hata) => {
        this.aktifProsesler.delete(isim);
        console.error('Proses hatası (' + isim + '):', hata.message);
        reject({ isim, hata: hata.message });
      });
    });
  }

  // Prosesi durdur
  durdur(isim, sinyal = 'SIGTERM') {
    const kayit = this.aktifProsesler.get(isim);
    if (!kayit) {
      console.log('Aktif proses bulunamadı: ' + isim);
      return false;
    }

    console.log('Proses durduruluyor: ' + isim + ' (PID: ' + kayit.pid + ', sinyal: ' + sinyal + ')');
    kayit.proses.kill(sinyal);
    return true;
  }

  // Tüm prosesleri durdur
  tumunuDurdur() {
    console.log('Tüm prosesler durduruluyor (' + this.aktifProsesler.size + ' adet)...');
    for (const [isim] of this.aktifProsesler) {
      this.durdur(isim);
    }
  }

  // Durum raporu
  durumRaporu() {
    console.log('
--- Proses Durum Raporu ---');
    console.log('Aktif prosesler: ' + this.aktifProsesler.size);

    for (const [isim, kayit] of this.aktifProsesler) {
      const sure = ((Date.now() - kayit.baslangic) / 1000).toFixed(1);
      console.log('  ▸ ' + isim + ' (PID: ' + kayit.pid + ', süre: ' + sure + 's)');
    }

    console.log('Tamamlanan prosesler: ' + this.tamamlananlar.length);
  }
}

// ===== PROSES PIPE ZİNCİRLEME =====

// İki prosesi pipe ile bağlama (Unix pipe gibi)
function prosesPipe(komut1, args1, komut2, args2) {
  return new Promise((resolve, reject) => {
    const proses1 = spawn(komut1, args1);
    const proses2 = spawn(komut2, args2);

    // Birinci prosesin çıktısını ikincinin girdisine bağla
    proses1.stdout.pipe(proses2.stdin);

    let sonuc = '';
    proses2.stdout.on('data', (veri) => {
      sonuc += veri.toString();
    });

    proses1.on('error', reject);
    proses2.on('error', reject);

    proses2.on('close', (kod) => {
      if (kod === 0) resolve(sonuc);
      else reject(new Error('Pipe zinciri başarısız, çıkış kodu: ' + kod));
    });
  });
}

// ===== KULLANIM ÖRNEKLERİ =====

async function main() {
  const yonetici = new ProsesYoneticisi();

  try {
    // Node.js betiği çalıştır
    const sonuc = await yonetici.baslat(
      'versiyon-kontrol',
      'node',
      ['-e', 'console.log(JSON.stringify(process.versions))'],
      { canliCikti: true }
    );
    console.log('Çıktı uzunluğu:', sonuc.stdCikti.length, 'karakter');

    // Durum raporu
    yonetici.durumRaporu();
  } catch (hata) {
    console.error('Ana işlem hatası:', hata);
  }
}

// Temiz kapanış sağla
process.on('SIGINT', () => {
  console.log('
Kapatma sinyali alındı...');
  process.exit(0);
});

basitKomut();
main().catch(console.error);

child_process.spawn, Node.js'in dış dünyayla etkileşim kurmasının en güçlü yoludur. Stream tabanlı çıktı işleme, sinyal yönetimi, pipe zincirleme ve detaylı proses kontrolü gibi özellikler, spawn'ı exec'e göre çok daha esnek ve güçlü kılar. Uzun süren işlemler, büyük çıktı üreten komutlar ve gerçek zamanlı veri işleme senaryolarında spawn her zaman ilk tercihiniz olmalıdır.
 

Şu an konuyu görüntüleyenler (Toplam : 0, Üye: 0, Misafir: 0)

Geri
Üst