وقتی قرار باشد در نرم‌افزار یک فرایند طولانی(مثل وارد کردن اطلاعات از فایل اکسل) انجام شود، بهتر است که وقت کاربر را نگیریم و به جای نشان دادن صفحه لودینگ این عملیات را در پشت صحنه انجام دهیم. در لاراوال این کار با استفاده queue یا صف انجام می شود.

با استفاده از queue دیگر لازم نیست که کاربر منتظر بماند تا یک عملیات سنگین در برنامه تمام شود و بعد به ادامه کارش بپردازد. می‌توانیم به او بگوییم که وقتی فرایند تمام شد با استفاده از نوتیفیکیشن او را خبر می‌کنیم. این‌طوری هم منابع سرور بهینه مصرف می‌شود و هم کاربر تجربه بهتری خواهد داشت.

در ادامه روش کار با queue در لاراول را یاد می‌گیریم.

انتخاب درایور برای اجرای queue ها

اول باید یک درایور برای queue انتخاب کنیم. درایور یعنی جایی که فرایندهای مورد نظرما در آن ذخیره می‌شوند به ترتیب (پشت سر هم مثل یک اعضای یک صف(queue))از آنجا خوانده و اجرا می‌شوند.

در اکثر موارد از database به عنوان درایور استفاده می‌کنیم پس مقدار QUEUE_CONNECTION در فایل env. را برابر database قرار دهیم.

QUEUE_CONNECTION=database

ایجاد جدول در دیتابیس

نیاز به یک جدول داریم که تمام مواردی که قرار است وارد queue شوند آنجا قرار بگیرد. پس دستورات زیر را اجرا می‌کنیم تا جداول ساخته شوند.

php artisan queue:table

php artisan migrate

یک جدول به نام jobs ساخته می شود تا تمام مواردی که که قرار است در پس زمینه نرم افزار اجرا شوند در این جدول ذخیره شود. به هر موردی که که قرار باشد توسط queue شود یک job یا وظیفه می گوییم. به خاطر همین اسم این جدول را jobs گذاشته اند.

درایور پیش فرض در لاراول sync است. این حالت به درد مواقعی می خوردکه در لوکال هاست کار می‌کنید. عملیاتی که قرار بود از طریق queue اجرا شود بلافاصله همزمان با ریکوئست اجرا می‌شود. چنین حالتی آنچیزی نیست که ما از queue انتظار داریم. قصد ما این است که فرایند در همان لحظه آغاز نشود و بعدا توسط سرور اجرا شود.

به همین دلیل اگر می‌خواهید مانند حالتی که در سرور واقعی رخ می دهد اینجا هم در پس زمینه فرایند انجام شود، مقدار driver را در فایل config/queue.php یا env تغییر دهید.

ساخت کلاس Job

برای اینکه یک عملیات را وارد queue کنیم باید یک کلاس به نام Job بسازیم. داخل این کلاس، فرایندی که قرار است در queue اجرا شود را می‌نویسیم.

ابن کلاس یک کلاس دیگر به نام Illuminate\Contracts\Queue\ShouldQueue را implement می‌کند.

دستور زیر چنین کلاسی را برای ما می‌سازد.

php artisan make:job ImportProducts

این کلاس در پوشه app\jobs به نامی که نوشتیم ایجاد می‌شود.

داخل کلاسی که ساخته شد یک متد handle وجود دارد. در این متد عملیات مورد نظرمان را می‌نویسیم.

اسم این کلاس را ImportProducts گذاشتم. هدف من این بوده که محصولات را از یک فایل اکسل وارد دیتابیس کنم. چنین فرایندی احتمالا زمان می‌برد. پس تصمیم گرفتم آن از طریق queue اجرا کنم.
صرفا برای نشان دادن مثال با استفاده از تابع sleep زمان انجام چنین فرایندی را شبیه سازی می‌کنم.

   public function handle()
    {
        sleep(10);

        Log::info('Import Compeleted');
    }

با این کار موقع اجرای این متد ابتدا 10 ثانیه طول می کشد و بعد باقی کد اجرا می شود و یک لاگ ثبت می‌شود.

اجرا کردن job ها

برای این که job هایی که ساختیم شروع به کار کنند باید آن ها را صدا بزنیم. برای این کار از متد dispatch استفاده می‌کنیم.

Route::get('/queue-test', function () {

    \App\Jobs\ImportProducts::dispatch();


});

با صدا زدن این متد این job در دیتابیس در جدول jobs قرار می‌گیرد و آماده اجرا خواهد بود. اینجا دیگر دستوراتی که در متد handle نوشتیم بلافاصله اجرا نمی شوند و کاربر نیازی نیست منتظر اجرای ریکوئست باشد.

اگر به جدول jobs نگاه کنید می بینید که یک ردیف جدید در آن ایجاد شده که اطلاعات job در آن ثبت شده است.

حالا برای این که job های ثبت شده در دیتابیس اجرا شوند از این دستور استفاده می کنیم:

php artisan queue:work

با این کار job ها یکی یکی اجرا می شوند. تا پردازش یک job تمام نشده، بعدی شروع نخواهد شد. دقیقا مثل یک صف.

اگر دوست دارید این فرایند با تاخیر اجرا شود از متد delay در ادامه آن استفاده کنید به این شکل:

ImportProduct::dispatch($products)->delay(now()->addHours(2));

با این کدی که بالا نوشتیم تا 2 ساعت بعد از dispatch شدن این job اجازه اجرا نخواهد داشت.

تعیین زمان timeout

برای اینکه از درگیر شدن بیش ازحد سرور جلوگیری کنیم می توانیم یک حداکثر زمان مشخص برای اجرای job تعیین کنیم. تا اگر در این مدت به خاطر خطا یا هر علتی دیگری، پردازش تمام نشد لاراول این job را متوقف کند.
برای این کار به کلاس Job یک پراپرتی public به نام $timeout اضافه می‌کنیم و مقدار آن به ثانیه برابر این زمان خواهد بود.

public $timeout = 60;

در کد بالا تعیین کردم که اگر بعد از گذشت 60 ثانیه فرایند تمام نشد پردازش متوقف می‌شود.

اجرای همه queue ها

اگر بخواهیم همه job ها را اجرا کنیم از این دستور استفاده می کنیم:

php artisan queue:work

با اجرای این دستور همه job هایی که در صف قرار گرفته اند به ترتسیب اجرا می شوند.

مدیریت job هایی که کامل اجرا نشدند

ممکن است بعضی از job ها به هر دلیلی حین اجرا دچار اشکال شده و متوقف شوند.
به طور پیش فرض یک migration برای ایجاد جدولی به نام failed_jobs در لاراول وجود دارد. داخل این جدول لیست همه job هایی که اجرا نشده اند ثبت خواهد شد. اگر این migration را در لاراول مشاهده نکردید با دستور زیر آن را اجرا کنید.

php artisan queue:faild-table

تعیین تعداد اجرا قبل از fail شدن

اگر بخواهید می توانید مشخص کنید اگر یک job اجرا نشد، لاراول تا چندبار اجرای آن را امتحان کند. برای این کار یک متغیر public به نام $tries در کلاس job تعریف کنید که مقدار آن برابر تعداد تکرار اجرای job است.

    public $tries = 3;

اجرای دوباره همه job های fail شده

با این دستور لاراول دوباره همه job هایی که دچار مشکل شدند را اجرا می‌کند.

php artisan queue:retry --all

اجرای یکی از job های fail شده

ممکن است نیاز داشته باشید فقط یکی از این job های fail شده را اجرا کنید برای این کار از دستور زیر به همراه ID آن job استفاده کنید

 php artisan queue:retry af11a89a-20c2-4756-996b-4ec8e6f66dd2

با دستور php artisan queue:failed لیست همه job های fail شده به همراه ID آن ها را می بینید که از آن می توانید برای اجرای دوباره آن job استفاده کنید.

failed jobs

جمع بندی

با چیزهایی که در این مطلب یادگرفتیم می توانیم فرایندهای زمان بر را به سیستم queue در لاراول بسپاریم تا وقت کاربر را نگیریم و منابع سرور را بهینه تر مصرف کنیم.
حواسمان هم باید به job هایی که fail می شوند باشد. باید برای اجرای دوباره آنها اقدام کنیم یا اینکه تصمیم دیگری با توجه به نوع نرم افزارمان بگیریم.