فرآیندهای فرزند (Child Processes)
Node.js، خود یک برنامه thread است، بنابراین به خوبی با single processes (یا فرآیندهای مجرد، فرایندهایی هستند که هنگام اجرا، فرایند جدیدی همانند خودش را ایجاد نمیکند) در CPU کار میکند اما یک فرآیند مجرد single process در یک CPU باعث می شوند که زمان بارگذاری سایت (load) افزایش و در نتیجه کارایی کاهش پیدا کند.
هر چقدر هم که سرور ما قوی و پیشرفته باشد باز هم یک فرایند تک نخی یا thread محدودیتهای خاص خودش را دارد و نمیتواند بارگذاری سایت را کنترل کند. علاوه بر این، درست است که node.js یک فرایند تک نخی است اما این به این معنی نیست که نمی تواند از مزیت های فرایندهای چندگانه (multiple process) بیبهره باشد.
ماژول child process در node.js به عنوان یک راه حل برای کمک به ما در زمینه ایجاد زیر فرایندها (sub-processes) یا فرآیندهای فرزند child processes از فرایند مجرد (single process) مورد استفاده قرار میگیرد، این کار باعث میشود که ما به راحتی بتوانیم مسئله بارگذاری سایت (load) را کنترل کنیم.
دقت نمایید که در اینجا فرایند مجرد (single process) که توانسته چند فرایند جدید همانند خودش ایجاد کند، با نام فرایند پدر شناخته می شود و فرایندهای ایجاد شده فرایند فرزند خوانده میشوند. این فرایندهای فرزند میتوانند با استفاده از ارتباط بین فرآیندی (inter process communication ) با همدیگر رابطه برقرار کنند.
دقت کنید که بر طبق گزارش ویکی پدیا، در علوم رایانه، ارتباطات بین پردازشی (به انگلیسی Inter-Process Communication یا IPC)، مجموعهای از روشها برای تبادل اطلاعات بین فرآیندهای فرزند child processes میباشند، و بنابراین هر یک از این روشها میتواند برای ارتباط بین یک یا چند فرآیند بکار میروند. ممکن است این فرآیندها در یک یا چندین رایانه متصل به هم توسط یک شبکه اجرا گردند.
ماژول child_process با اجرای دستور OS در فرآیندهای فرزند، میتواند به ویژگیها و قابلیتهای سیستم عامل دسترسی پیدا کند. دقت کنید که ما میتوانیم جریان ورودی (input stream) و جریان خروجی (output stream) فرایندهای فرزند را به خوبی اداره کنیم.
ما همچنین میتوانیم از آرگومانها دستور OS برای کنترل بیشتر فرآیندها نیز، استفاده نماییم. همچنین مؤلفههای ارتباطی (pipes) برایstdin ، stdout و stderr بین گره فرزند و گره پدر ایجاد شده است.
نحوی ایجاد فرایندهای فرزند Child processes
چهار روش مختلف برای ایجاد فرایندهای فرزند Child processes وجود دارد:
- ()spawn
- ()fork
- ()exec
- ()execFile
فرایندهای فرزند میتوانند به صورت غیر همزمان (Asynchronously) و یا به صورت همزمان (Synchronously) بر اساس الزامات ایجاد شود.
- متدهای غیر همزمان (Asynchronously) فرایندهای فرزند را به صورت غیر متوقف کننده (در این روش فرآیندهای فرزند معطل فرآیند فرزند دیگر نمی شوند) در یک حلقه رویداد (event loop) ایجاد میکنند. جهت یادآوری باید خدمت شما عرض کنم که یک رویداد (event) میتواند به وسیله کامپیوتر و یا کاربر و یا … ایجاد شود، در واقع event loop صفی از callback function ها هست. این صف از نوع FIFO هست و همان طور که میدانید داخل این صف هر چیزی اول وارد شود، اول خارج میشود. به عنوان مثال وقتی ما بر روی صفحه کلیک می کنیم، داریم بک عضو به event loop اضافه میکنیم.
- متدهای همزمان (Synchronously) فرایندهای فرزند را ایجاد میکنند، اما تا زمانی که فرایند فرزندی از حلقه رویداد (event loop) خارج نشود یا خاتمه نیابد، هیچ فرآیند فرزند دیگری نمی تواند اجرا شود. در موارد خاصی، فرایندهای فرزند حاصل از متدهایی همزمان (Synchronously) راحتتر استفاده میشوند. با این حال در بسیاری از موارد، متدهای همزمان بر روی عملکرد سیستم تأثیر گذارند زیرا حلقه ی رویداد را متوقف میکنند.
اجازه دهید که برای درک بهتر شما عزیزان، جز به جز هر یک از متدهای ایجاد فرایند را با هم بررسی کنیم.
ایجاد فرآیندها به صورت غیر همزمان (Asynchronously)
چهار راه مختلف برای ایجاد فرایندهای فرزند (Child processes) به صورت غیر همزمان وجود دارد:
()child_process.spawn
: این متد فرایند فرزند را به صورت غیرهمزمان و بدون ایجاد توقف در حلقه رویداد node.js ایجاد میکند.()child_process.fork
: این متد برای ایجاد یک فرایند node.js جدید استفاده میشود و همچنین یک متد خاص را فراخوانی میکند که کار این متد خاص فراهم کردن کانال ارتباطات بین پردازشی (که پیشتر آن را توضیح دادم) هست که به ما اجازه ارسال پیام بین فرایند پدر و فرایند فرزند را میدهد.()child_process.exec
: این متد برای تولید کردن یک sell و سپس اجرای دستور در آن sell مورد استفاده قرار میگیرد. این متد همچنین اجاره میدهد، زمانی که اجرای فرآیند تکمیل شد، یک تابع Callback اختیاری فراخوانی شود.()child_process.execFile
: این متد از لحاظ کارایی مشابه متد ()child_process.exec است با این تفاوت که به جای ایجاد یک sell ، این اجازه را میدهد که دستورات به صورت مستقیم و به عنوان یک آرگومان در آن وارد شوند.
نکات زیر جهت یادآوری عرض می شود که:
- در تعریف کلی Shell یک رابط دستوری بین کاربر و هسته سیستم عامل هست. در واقع Shell یک قطعه نرمافزاری است که به کاربر اجازه دسترسی مستقیم به سیستم عامل را میدهد. دستورات در کامپیوتر به صورت 0 و 1 شناخته میشوند، اما اگر ما بخواهیم برای ارتباط با کامپیوتر و اعلام دستورات به آن از کد 0 و 1 یا زبان اسمبلی استفاده کنیم بسیار دشوار خواهد بود. در نتیجه Shell ها به وجود آمدهاند که با دریافت دستوراتی مشخص آنها را برای کامپیوتر ترجمه نمایند. Shell ها یا به صورت رابط خط دستور (Command-line Interface (CLI و یا به صورت رابط گرافیکی (Graphical User Interface (GUI میباشند.
- در جاوا اسکریپت این امکان وجود دارد که یک تابع را به عنوان آرگومان ورودی به تابعی دیگر ارسال کرد. به تابعی که به عنوان آرگومان ورودی به تابعی دیگر ارسال میشود، تابع Callback یا Callback Function گفته میشود.
اجازه دهید که با استفاده از مثال، جزییات هر کدام از متدهای غیر همزمان را بیشتر درک کنیم.
1- ()child_process.spawn
همان طور که گفته شد این متد برای ایجاد یک فرایند جدید مورد استفاده قرار میگیرد.
نحوی نوشتن این متد:
child_process.spawn(command[, args][, options])
پارامترها :
- command : این پارامتر از نوع رشته است. در واقع فرمانی که باید اجرا شود را مشخص میکند.
- args : این پارامتر از نوع آرایه است. در واقع باید لیستی از آرگومانهای رشتهای را مشخص کنیم.
- object : این پارامتر از نوع اشیاء است و میتواند شامل یک یا بیشتر از موارد زیر باشد:
- cwd
- env
- argv0
- stdio
- detached
- uid
- gid
- shell
- windowsVerbatimArguments
- windowsHide
مقدار برگشت داده شده توسط این متد child_process.spawn : یک فرآیند فرزند childProcess است.
مثال کد نویسی: ما دو فایل master-spawn.js و slave.js را برای شما در زیر آماده کردهایم.
//Name of the file : slave.js console.log("Child Process number " + process.argv[2] + " is executed." );
//Name of the file : master-spawn.js var cp = require('child_process'); for(var i = 1; i<6; i++) { var worker = cp.spawn('node', ['slave.js', i]); worker.stdout.on('data', function (data) { console.log('Value of Stdout : ' + data); }); worker.stderr.on('data', function (data) { console.log('stderr: ' + data); }); worker.on('close', function (code) { //console.log("Exit code : " + code); console.log('child process exited with code '); }); }
حالا ما میتوانیم مانند تصویر زیر مثالهای بالا را اجرا نماییم.
2- ()child_process.fork
این متد نیز یک فرآیند جدید را ایجاد میکند به موارد زیر دقت نمایید.
نحوی نوشتن این متد:
child_process.fork(modulePath[, args][, options])
پارامترها:
- modulePath : این پارامتر از نوع رشته است و دستوری که باید اجرا شود در آن قرار میگیرد.
- args : این پارامتر از نوع آرایه است و لیستی از آرگومانهای رشته را شامل میشود.
- options : این پارامتر از نوع اشیاء است و میتواند شامل یک یا بیشتر از موارد زیر باشد:
- cwd
- env
- execPath
- execArgv
- silent
- stdio
- uid
- windowsVerbatimArguments
- windowsHide
مقدار برگشت داده شده توسط این متد: یک childProcess است.
مثال کد نویسی: ما دو فایل master-fork.js و slave.js را برای شما در زیر آماده کردهایم.
//Name of the file : slave.js console.log("Child Process number " + process.argv[2] + " is executed." );
//Name of the file : master-fork.js var cp = require('child_process'); for(var i=1; i<6; i++) { var worker = cp.fork("slave.js", [i]); worker.on('close', function (code) { console.log('child process exited with code ' + code); }); }
حالا ما میتوانیم مانند تصویر زیر مثالهای بالا را اجرا نماییم.
3- ()child_process.exec :
این متد نیز یک فرآیند جدید را ایجاد میکند به موارد زیر دقت نمایید.
نحوی نوشتن این متد:
child_process.exec(command[, options][, callback])
پارامترها:
- command : این پارامتر از نوع رشته است و دستوری که باید اجرا شود در آن قرار میگیرد.
- options : این پارامتر از نوع اشیاء است و میتواند شامل یک یا بیشتر از موارد زیر باشد:
- cwd
- env
- encoding
- shell
- timeout
- maxBuffer
- killSignal
- uid
- gid
- windowsHide
callback :
- error
- stdout
- stderr
مقدار برگشت داده شده توسط این متد: یک childProcess است.
مثال کد نویسی: ما دو فایل master-exec.js و slave.js را برای شما در زیر آماده کردهایم.
//Name of the file : slave.js console.log("Child Process number " + process.argv[2] + " is executed." );
// Name of the file : master-exec.js var cp = require('child_process'); for(var i=1; i<6; i++) { var workerProcess = cp.exec('node slave.js ' + i , function(error, stdout, stderr) { if (error) { console.log(error.stack); console.log('Error Code: '+error.code); console.log('Error Signal: '+error.signal); } if(stderr){ console.log('value of stderr: ' + stderr); } console.log('Value of stdout: ' + stdout); }); workerProcess.on('exit', function (code) { //console.log("exit code : "+ code); console.log('Child process exited '); }); }
حالا ما میتوانیم مانند تصویر زیر مثالهای بالا را اجرا نماییم.
4- ()child_process.execFile :
این متد نیز یک فرآیند جدید را ایجاد میکند به موارد زیر دقت نمایید.
نحوی نوشتن این متد:
child_process.execFile(file[, args][, options][, callback])
پارامترها:
- file : این پارامتر از نوع رشته است و دستوری که باید اجرا شود در آن قرار میگیرد.
- args: لیستی از آرگومانهای از نوع رشته.
- options : این پارامتر از نوع اشیاء است و میتواند شامل یک یا بیشتر از موارد زیر باشد:
- cwd
- env
- encoding
- timeout
- maxBuffer
- killSignal
- uid
- gid
- windowsHide
- windowsVerbatimArguments
callback :
- error
- stdout
- stderr
مقدار برگشت داده شده توسط این متد: یک childProcess است.
مثال کد نویسی: در پایین، یک قطعه کد ساده را مشاهده کنید که با استفاده از متد execFile، برای شما نسخهی node.js نصب شده بر روی سیستم تان را به نمایش در میآورد.
var ef = require('child_process').execFile; var child = ef('node', ['--version'], (err, stdout, stderr) => { if (err) { console.log('stderr', stderr); throw err; } console.log('Node.js version : ', stdout); });
ایجاد فرآیندها به صورت همزمان (Synchronously)
در اینجا ما برای شما سه راه مختلف ایجاد فرایندهای فرزند (child processes) به صورت همزمان (Synchronously) را آوردهایم:
()spawnSync
:این متد برای شما فرایند فرزند را به صورت همزمان ایجاد میکند اما باید دقت کنید که برای انجام این کار، event loop (حلقه رویداد) برنامه node.js را متوقف میکند.()execSync
: این متد همانند متد child_process.exec() هست با این تفاوت که به صورت همزمان اجرا می شود، بنابراین event loop (حلقه رویداد) برنامه node.js را متوقف میکند و باعث توقف در اجرای هر کد دیگری میشود.()execFileSync
: این متد همانند متد child_process.execFile() هست با این تفاوت که به صورت همزمان اجرا میشود، بنابراین event loop (حلقه رویداد) برنامه node.js را متوقف میکند و باعث توقف در اجرای هر کد دیگری میشود.
رویدادهای موجود در ماژول child process
رویدادهایی که هنگام کار با فرآیندهای فرزند روی می دهند، عبارتاند از:
Message
: این رویداد هنگامی رخ میدهد که یک فرایند فرزند (child process) از متد ()process.send برای ارسال پیامها از طریق ارتباط IPC ( ارتباط بین فرآیندی inter process communication ) استفاده میکند.exit
: این رویداد زمانی روی میدهد که فرآیندهای فرزند خاتمه پیدا کنند.error
: رویداد error در 3 حالت زیر ممکن است روی دهد:
1- وقتی که فرآیند فرزند نمیتواند ایجاد شود.
2- وقتی که فرایند فرزند خاتمه نمییابد
3- زمانی که ارتباط با فرآیند فرزند امکانپذیر نباشد.
disconnect
: زمانی روی میدهد که فرایند پدر متد process.disconnect() یا متد subprocess.disconnect() را فراخوانی میکند. دقت نمایید اگر رویداد disconnect به وقوع بپیوندد دیگر نمیتوان از ارتباط IPC استفاده کرد.close
: زمانی روی میدهد که فرایند فرزند، stdio streams را مسدود میکند.
خلاصه
در درس 24ام از سلسله دروس 30 روز با نود جی اس، اطلاعات زیادی در مورد فرآیندهای فرزند child processes را با همدیگر یاد گرفتیم. همچنین آموختیم که چگونه این فرآیندهای فرزند را میتوان به صورت همزمان و غیر همزمان ساخت.