در پی اچ پی نسخه ی 7 به بعد با اکستنشن pthread می توانیم برای سریعتر شدن اجرای کارهای طولانی کار را بین چندین نخ تقسیم کنیم. اما اگر این اکستنشن روی سرور نصب نباشد چه باید کرد؟
همانطور که در مقاله ی قبل هم گفته شد می توانیم پردازش موازی را بصورت multi-process داشته باشیم. برای مثال قصد دارید بالای 100 هزار ایمیل را ارسال کنید، این بهترین مثال برای استفاده از multi processing در پی اچ پی است.
برای Execute Command می توانیم بغیر از exec، exec_shell یا passthru از کامپوننت Process که در سیمفونی معرفی شد در پروژه ی لاراولی خود استفاده کنید چرا که لاراول برخی از کتابخانه ها و کامپوننت های سیمفونی را در خود جا داده است و کامپوننت Process نیز یکی از آنهاست.
شاید با خود بگویید که چرا از job ها در لاراول استفاده نکنیم، دلیل اول اینکه مسئله ی timeout را داریم اگر بخواهیم بصورت Sync اجرا کنیم و دلیل دوم اینکه نباید 100 هزار رکورد را در جدول jobs وارد کنیم. علاوه بر این هر job برای اتصال به دیتابیس بحث connect/disconnect را دارد که خود باعث سربار شده و کارایی را پایین می آورد.
ایجاد فرایند:
فرض کنید که شما قصد دارید در کنترلر خود یک کامند را در background اجرا کنید، شما به سادگی می توانید یک نمونه از کلاس Process را ایجاد کنید.
$process = new Process('php ' . base_path('artisan') . ' task:long-running-task &'); $process>setTimeout(0); $process->run();
توجه داشته باشید که از متود run استفاده کرده ایم که بصورت synchronous&blocking است. اما ما asynchronous می خواهیم. شما می توانید از متود start بجای متود run استفاده کنید که asynchronous است اما فرایند سریعا بعد از اینکه کنترلر پاسخ را به سمت کلاینت برگرداند kill میشود و تسک شما کامل نمی شود.
بگذارید ابتدا دلیل kill شدن را توضیح دهم زمانیکه متود start اجرا میشود، کامند در زمینه اجرا میشود اما وقتی فرایند اصلی که در اینجا کنترلر است پاسخ را ارسال می کند کارش تمام شده و تمامی sub-process های آن هم kill می شوند و این دلیل kill شدن فرایند background ماست.
اهمیت “&” :
اگر توجه داشته باشید یک علامت “&” در انتهای دستور گذاشته ایم
‘php ‘ . base_path(‘artisan’) . ‘ task:long-running-task &’
که نقش بسیار مهمی را ایفا میکند. & باعث اجرای فرایند شما را در background می شود(مثل daemon) و فرایند شما بعنوان یک فرایند جداگانه تلقی می شود. اتمام فرایند اصلی تاثیری روی فرایند daemon/background ما نخواهد داشت.
با این کار کامند شما یک فرایند اصلی است و می توانید چندید فرایند را برای انجام کارتان به اجرا در بیاورید.
for ($i = 0; $i < $numberOfProcess; $i++) { $process = new Process('php ' . base_path('artisan') . " task {$i}"); $process->setTimeout(0); $process->disableOutput(); $process->start(); $processes[] = $process; }
همانطور که میبینید از متود start برای ایجاد sub-process ها استفاده کرده ایم تا عملیات بصورت asynchronous اجرا شود. نکته ی مهم در اینجا این است که این کد که در واقع در حال حاضر فرایند اصلی ما بحساب می رود نباید قبل از اتمام sub-process ها تمام شود و باید تا کامل شدن تسک ها ی دیگر صبر کند.
for ($i = 0; $i < $numberOfProcess; $i++) { ... // above code } // wait for above processes to complete while (count($processes)) { foreach ($processes as $i => $runningProcess) { // specific process is finished, so we remove it if (! $runningProcess->isRunning()) { unset($processes[$i]); } sleep(1); } } //Executes after all processes are done with their task
بصورت خلاصه در ذهن داشته باشید که main process نباید تمام یا کشته شود تا child process ها زنده مانده و به کار خود ادامه دهند در غیر اینصورت child process ها هم کشته می شوند.
پیروز باشید...