child_process — exec, spawn, fork
Muammodan boshlaylik
Node.js bitta thread da ishlaydi — bu uning kuchi, lekin ba'zan cheklov. Tasavvur qiling:
- Siz Node.js serverida foydalanuvchi yuklagan rasmni qayta ishlashingiz kerak — ImageMagick bilan
- Yoki Python da yozilgan ML modelini ishga tushirishingiz kerak
- Yoki tizim buyrug'ini bajarishingiz kerak:
git pull,ffmpeg,zip
Bularni Node.js ning o'zida qilish mumkin emas yoki samarasiz. Yechim — child process: alohida jarayon yaratib, u yerda bajarish.
Xuddi siz ish beruvchi bo'lsangiz va bir ishni xodimga topshirsangiz — siz kutib o'tirmaysiz, boshqa ishingizni qilasiz. Xodim tugagach xabar beradi.
child_process moduli
const { exec, spawn, fork } = require('child_process');
Uch xil funksiya — har biri boshqacha holat uchun:
exec → qisqa buyruq, natijani bir yo'la olish
spawn → uzun jarayon, output ni stream bilan olish
fork → Node.js skriptini alohida jarayonda ishlatish
exec — qisqa buyruqlar uchun
exec — buyruqni shell orqali bajaradi va natijani bir yo'la qaytaradi. Kichik natijalar uchun qulay.
const { exec } = require('child_process');
exec('ls -la', (err, stdout, stderr) => {
if (err) {
console.error('Xato:', err.message);
return;
}
if (stderr) {
console.error('Stderr:', stderr);
}
console.log(stdout);
});
stdout — buyruqning oddiy chiqishi. stderr — xato chiqishi. Ba'zi buyruqlar xato bo'lmasa ham stderr ga yozadi — shuning uchun err va stderr ni alohida tekshirish kerak.
util.promisify bilan async/await uslubida:
const { exec } = require('child_process');
const { promisify } = require('util');
const execAsync = promisify(exec);
async function getGitLog() {
try {
const { stdout } = await execAsync('git log --oneline -5');
console.log(stdout);
} catch (err) {
console.error('Git xatosi:', err.message);
}
}
exec ning cheklovi: natija xotiraga to'liq yuklanadi. Standart limit — 1 MB. Katta natija uchun spawn ishlatiladi.
spawn — katta natijalar va uzun jarayonlar uchun
spawn — buyruqni ishga tushiradi va output ni stream orqali beradi. Xotira muammosi yo'q, jarayon ishlayotganda ham natijani olish mumkin.
const { spawn } = require('child_process');
const ls = spawn('ls', ['-la', '/home']);
ls.stdout.on('data', (data) => {
process.stdout.write(data); // kelgan bo'lakni darhol chiqar
});
ls.stderr.on('data', (data) => {
console.error('Xato:', data.toString());
});
ls.on('close', (code) => {
console.log(`Jarayon ${code} kodi bilan tugadi`);
});
exec dan farqi: argumentlar massiv sifatida beriladi — shell injection xavfi yo'q. exec da 'ls ' + userInput xavfli bo'lishi mumkin.
Amaliy misol — katta faylni zip qilish:
const { spawn } = require('child_process');
function zipFile(input, output) {
return new Promise((resolve, reject) => {
const gzip = spawn('gzip', ['-c', input]);
const out = require('fs').createWriteStream(output);
gzip.stdout.pipe(out);
gzip.on('close', (code) => {
if (code === 0) resolve();
else reject(new Error(`gzip ${code} kodi bilan tugadi`));
});
gzip.on('error', reject);
});
}
await zipFile('big-file.txt', 'big-file.txt.gz');
fork — Node.js skriptlarini alohida jarayonda ishlatish
fork — spawn ning maxsus versiyasi, faqat Node.js skriptlari uchun. Asosiy jarayon va child jarayon o'rtasida xabar almashish (IPC) imkoni bor.
Nima uchun kerak? Node.js bitta thread da ishlaydi. Og'ir hisoblash (katta massivni saralash, kriptografiya, rasm qayta ishlash) asosiy thread ni bloklaydi — server boshqa so'rovlarga javob bera olmaydi.
Yechim: og'ir ishni alohida jarayonga topshirish.
// worker.js — alohida jarayon
process.on('message', (data) => {
// Og'ir hisoblash:
const result = heavyCalculation(data);
process.send({ result }); // asosiy jarayonga qaytarish
});
function heavyCalculation(n) {
let sum = 0;
for (let i = 0; i < n; i++) sum += i;
return sum;
}
// main.js — asosiy jarayon
const { fork } = require('child_process');
const worker = fork('./worker.js');
// Xabar yuborish:
worker.send(1_000_000_000);
// Natijani olish:
worker.on('message', ({ result }) => {
console.log('Natija:', result);
worker.kill(); // ishlatib bo'lgach to'xtat
});
worker.on('error', (err) => {
console.error('Worker xatosi:', err.message);
});
// Asosiy jarayon bloklanmaydi — boshqa so'rovlarga javob beradi
console.log('Worker ishlamoqda, men boshqa ishni qilaman...');
exec vs spawn vs fork — qachon qaysi?
Hayotiy o'xshatma bilan tushuntirsak:
- exec — do'stingizga "menga non olib kel" deyasiz, u olib kelgach aytadi. Kichik ish, bir yo'la natija.
- spawn — fabrikaga buyurtma berasiz, ular ishlab chiqargan sayin yubora boshlaydi. Katta ish, natija asta-sekin keladi.
- fork — o'z xodimingizni yuborasiz, u bilan telefon orqali gaplashib turasiz. Node.js skripti, ikki tomon xabar almashadi.
Holat | Tavsiya
-----------------------------------|--------
Tizim buyrug'i, kichik natija | exec
Tizim buyrug'i, katta natija | spawn
Node.js skripti, og'ir hisoblash | fork
Tez-tez uchraydigan xatolar
exec da foydalanuvchi kiritmasini to'g'ridan-to'g'ri ishlatish:
// Xato — shell injection:
const userInput = '; rm -rf /'; // zararli kiritma
exec(`ls ${userInput}`); // 'ls ; rm -rf /' — xavfli!
// To'g'ri — spawn bilan massiv:
spawn('ls', [userInput]); // argument sifatida — xavfsiz
Child process ni to'xtatmaslik:
const worker = fork('./worker.js');
worker.send(data);
worker.on('message', (result) => {
console.log(result);
// worker.kill() unutildi → jarayon "zombie" bo'lib qoladi
});
Ishlatib bo'lgach worker.kill() yoki worker.disconnect() chaqiriladi.
exec da katta natija:
// 100 MB natija qaytaradigan buyruq:
exec('cat huge-file.txt', (err, stdout) => {
// stdout — 100 MB xotiraga yuklanadi
// Error: stdout maxsize exceeded
});
// To'g'ri — spawn ishlatiladi:
const cat = spawn('cat', ['huge-file.txt']);
cat.stdout.pipe(process.stdout);
Xulosa
child_process — Node.js dan tashqaridagi dunyoga chiqish eshigi. Tizim buyruqlari, boshqa tillar, og'ir hisoblashlar — bularning hammasi alohida jarayonda bajariladi, asosiy server bloklanmaydi.
exec — qisqa buyruqlar uchun, natija bir yo'la. spawn — uzun jarayonlar va katta natijalar uchun, stream bilan. fork — Node.js skriptlari uchun, IPC bilan xabar almashish.
Keyingi dars
22-dars: worker_threads va cluster — CPU intensive vazifalar