تذکر: این یک پست آموزشی برای عموم نیست! بلکه تنها جایی برای یادداشتهای من حین یادگیریه تا بهتر به خاطر بسپارم و در صورت لزوم به اونها مراجعه کنم.
قسمت ۱۲۴ تا ۱۲۹
در واقع Job معادل با کار و پروسهای هست که قراره انجام بشه و Queue معادل لیست کارهایی هست که قراره انجام بشه. چرا از Job استفاده میکنیم؟
یکی از دلایلش میتونه موکول کردن کارهای CPU Bound و... به زمانی هست که فشار کمتری روی سیستمه. مثلا Generate کردن یک گزارشگیری خیلی بزرگ از دیتابیس، در واقع میخوایم Background Processing انجام بدیم.
یکی از دلایل دیگه اینه که قراره یک کاری، در یک زمان خاص انجام بشه، اینطوری می تونیم یک پروسه رو در یک زمان مشخص ران کنیم.
تنظیمات درایور Queue ما در پوشهٔ config و فایل queue.php قرار داره، پارامتر default این فایل میاد و از env متغیر QUEUE_CONNECTION رو میخونه که به صورت پیشفرض روی sync قرار داره، این مقدار بیانگر اینه که Job در همان لحظه ران میشه و به زمان بعدی موکول نمیشه.
کانکشن بعدی از نوع database هست که روی این مورد کار میکنیم؛ سرویسهای خارجی هم برای این کار وجود داره که فعلا وارد بحث با اونا نمیشیم.
توی queue.php بخش faild رو هم داریم که Jobهایی که ران نشدن یا با مشکل مواجه شدن رو برای بررسی بعدی ذخیره میکنه.
php artisan make:job TestJob
با دستور بالا داخل پوشهٔ app یک پوشهٔ جدید به نام Jobs ایجاد میشه که Jobهای ما داخل اون قرار میگیره، الان هم با دستور بالا فایل TestJob.php داخل این پوشه ایجاد شده:
کلاس Job ما از اینترفیس ShouldQueue استفاده کرده و ۴ مورد رو use کرده:
۱- Dispatchable: قابلیت fire یا dispatch شدن رو به جاب ما میده
۲- InteractWithQueue: تعامل بین Job و Queue
۳- Queueable: قابلیت قرار گرفتن Job در درون صفهای ما
۴- SerializeModels: نوعی فرمت Data مثل Json، علت استفاده از Serialize اینه که اطلاعات Jobها رو ذخیره کنیم؛ و اگر بعد از ۲ ساعت که جاب ران شد ولی fail شد؛ بتونیم اون دیتا رو داشته باشیم تا دوباره ازش استفاده کنیم.
کار اصلی داخل متد handle جاب ما رخ میده، یعنی وقتی نوبت اجرای یک جاب داخل صف رسید؛ متد handle اون جاب فراخوانی میشه و کارش رو انجام میده.
برای فراخوانی و fire کردن یک job میتونیم توی جای مد نظر، مثلا توی Controller اینطوری رانش کنیم:
TestJob::dispatch();
حالا اگر داخل متد handle مقداری رو dd کرده باشیم؛ خروجی رو برای ما نشون میده. علت این که سریعا dd اتفاق افتاده اینه که تنظیمات درایور روی sync قرار داره و به معنی اجرای فوری هست؛ برای زمان دار کردن Job میاییم و از database یا سایر سرویسهای خارجی استفاده میکنیم که اینجا ما از database استفاده میکنیم.
برای این که درایور Queue رو روی database قرار بدیم؛ باید داخل فایل env مقدار QUEUE_CONNECTION رو به database تغییر بدیم. حالا جابهای ما توی table ای به اسم jobs ذخیره میشن، ما در حالت پیش فرض این table وجود نداره و ما باید از artisan برای ایجادش کمک بگیریم:
php artisan queue:table
با اجرای دستور بالا، جدول مایگریشن جدول jobs ما ایجاد میشه:
۱- فیلد queue: دستهبندی جابهای ما
۲- payload: قرار گرفتن اطلاعات Serialize شده
۳- attemps: دفعات تلاش برای ران شدن
۴- reserved_at: تاریخ اجرا
۵- available_at: زمان در دسترس بودن جاب
۶- created_at: زمان ایجاد شدن جاب
حالا اگر یک بار دیگه dispatch کنیم؛ میبینیم که داخل جدول jobs جاب ما ایجاد شده، اما ران نشده:
مقدار فیلد queue به صورت پیش فرض روی default هست؛ ما میتونیم جابهای خودمون رو دستهبندی کنیم و برای تعیین دستهبندی از این فیلد استفاده میکنیم. مثلا ممکنه جاب ما از نوع email باشه و...
فیلد بعدی ما payload هست که اینطوریه:
کمکی که دیتای Serialize شده به ما میکنه، اینه که می تونیم کلاس، تابع و موارد این چنین رو به راحتی توی دیتابیس ذخیره و بازیابی کنیم. در واقع به کار بازیابی اون کلاس یا تابع و... Unserialize کردن گفته میشه.
برای صدا زدن این جاب ذخیره شده داخل دیتابیس باید از artisan کمک بگیریم:
php artisan queue:work --queue=default
توی دستور بالا، مقدار --queue رو برابر با default قرار دادیم، این دیفالت در واقع همون مقداری هست که فیلد queue که نوع جاب رو مشخص میکنه (توی jobs table)، حالا اگر جاب ما از دستهبندی دیگه ای باشه، کافیه به جای default اون دستهبندی مثلا email رو قرار بدیم.
بعد از اجرا کردن دستور بالا، جاب ما میره برای پردازش و dispatch شدن:
نکتهٔ جالب اینه که بعد از پردازش شدن یک جاب، اون جاب از جدول jobs حذف میشه.
php artisan make:job TestJobEmail
حالا میریم برای dispatch کردن این جاب طبق دستهبندی خاص خودمون، برای این کار:
TestJobEmail::dispatch()->onQueue('email');
و حالا توی جدول این میشه:
حالا به راحتی میتونیم رانش کنیم:
php artisan queue:work -queue=email
اما اگر دستور queue:work رو بدون فلگ --queue ران کنیم؛ در واقع queue های داخل دستهبندی دیفالت ران خواهند شد.
حالا اگر قرار باشه job رو با تاخیر ران کنیم؛ باید از delay استفاده کنیم:
در واقع job زمانی اجرا میشه که توی available_at اومده.
برای اجرای job در لحظه، میتونیم از دستور زیر استفاده کنیم:
اگر به هر دلیلی خطایی رخ دهد و یک Job اجر نشود؛ این جاب در جدول failed_jobs داخل دیتابیس لیست میشود.
خب برای تست، میتونیم به راحتی یک Exception رو داخل متد handle ران کنیم؛ اینطوری جاب ما failed میشه و جدول failed_jobs ما مقدار میگیره:
وقتی queue:work رو ران می کنیم؛ این سرویس مدام در حال پایش هست که اگر job جدیدی اضافه شد؛ به موقع اون رو ران کنه، با ران شدن یک جاب، اون جاب از جدول jos کنار گذاشته میشه و اگر دچار اختلال بشه، میره و توی failed_jobs قرار میگیره.
علاوه بر اینکه می تونیم جابهای failed شده رو توی یک View به نمایش بذاریم (طبیعتا روتر، کنترلر و ویو خودش رو میطلبه)، میتونیم از دستور زیر هم کمک بگیریم:
php artisan queue:failed
که این میشه خروجیش:
خود queue:work چندین فلگ داره، که یکی از این flagها tries هست:
php artisan queue:work --tries=3
اگر بخوایم برای یک جاب tries اختصاصی تعریف کنیم باید داخل کلاس اون جاب این کار رو انجام بدیم، چون فلگ tries روی queue:work روی تمامی جابها عمل می کنه.
php artisan queue:retry id/all
دستور retry جاب رو از جدول failed خارج و دوباره وارد صف jobs میکنه... حالا اگر queue:work بزنیم و مشکل خاصی در اجرا نباشه، اجرا میشه و اگر هم مشکلی داشته باشه، دوباره از jobs وارد failed_jobs میشه.