نسخه:

صف ها

معرفی

در حین ساختن برنامه وب خود، ممکن است وظایفی مانند تجزیه و ذخیره یک فایل CSV آپلود شده داشته باشید که انجام آنها در طول یک درخواست وب معمولی خیلی طول می کشد. خوشبختانه، لاراول به شما اجازه می دهد تا به راحتی مشاغل در صف ایجاد کنید که ممکن است در پس زمینه پردازش شوند. با انتقال وظایف زمان بر به صف، برنامه شما می تواند به درخواست های وب با سرعتی خیره کننده پاسخ دهد و تجربه کاربری بهتری را برای مشتریان شما فراهم کند.

صف‌های لاراول یک API صف یکپارچه را در میان انواع پشتیبان‌های صف مختلف، مانند Amazon SQS ، Redis ، یا حتی یک پایگاه داده رابطه‌ای ارائه می‌کنند.

گزینه های پیکربندی صف لاراول در config/queue.php فایل پیکربندی برنامه شما ذخیره می شود. در این فایل، پیکربندی‌های اتصال برای هر یک از درایورهای صف را که با فریم‌ورک موجود است، از جمله درایورهای پایگاه داده، Amazon SQS ، Redis و Beanstalkd و همچنین یک درایور همزمان که وظایف را فوراً اجرا می‌کند (برای استفاده در طول) پیدا خواهید کرد. توسعه محلی). یک null راننده صف نیز گنجانده شده است که مشاغل در صف را کنار می گذارد.

لاراول اکنون Horizon، داشبورد و سیستم پیکربندی زیبا را برای صف‌های مجهز به Redis شما ارائه می‌کند. برای اطلاعات بیشتر ، مستندات کامل Horizon را بررسی کنید .

اتصالات در مقابل صف ها

قبل از شروع کار با صف های لاراول، مهم است که تمایز بین "اتصالات" و "صف" را درک کنید. در فایل پیکربندی شما config/queue.php ، یک آرایه پیکربندی وجود دارد connections . این گزینه اتصالات به خدمات صف پشتیبان مانند Amazon SQS، Beanstalk یا Redis را تعریف می کند. با این حال، هر اتصال صف داده شده ممکن است چندین "صف" داشته باشد که ممکن است به عنوان پشته های مختلف یا انبوهی از کارهای صف در نظر گرفته شود.

توجه داشته باشید که هر مثال پیکربندی اتصال در queue فایل پیکربندی حاوی یک queue ویژگی است. این صف پیش‌فرض است که کارها هنگام ارسال به یک اتصال خاص به آن ارسال می‌شوند. به عبارت دیگر، اگر یک کار را بدون مشخص کردن صریحاً مشخص کنید که باید به کدام صف ارسال شود، آن کار در صفی که در queue ویژگی پیکربندی اتصال تعریف شده است قرار می‌گیرد :

use App\Jobs\ProcessPodcast;
 
// This job is sent to the default connection's default queue...
ProcessPodcast::dispatch();
 
// This job is sent to the default connection's "emails" queue...
ProcessPodcast::dispatch()->onQueue('emails');

برخی از برنامه‌ها ممکن است نیازی به فشار دادن کارها به چند صف نداشته باشند، در عوض ترجیح می‌دهند یک صف ساده داشته باشند. با این حال، فشار دادن کارها به صف های متعدد می تواند به ویژه برای برنامه هایی مفید باشد که مایلند نحوه پردازش کارها را اولویت بندی یا تقسیم بندی کنند، زیرا کارگر صف لاراول به شما اجازه می دهد تا مشخص کنید کدام صف ها را باید بر اساس اولویت پردازش کند. به عنوان مثال، اگر مشاغل را به یک high صف فشار دهید، ممکن است کارگری را اجرا کنید که به آنها اولویت پردازش بالاتری می دهد:

php artisan queue:work --queue=high,default

نکات و پیش نیازهای راننده

پایگاه داده

برای استفاده از database درایور صف، به یک جدول پایگاه داده برای نگهداری کارها نیاز دارید. برای ایجاد مهاجرتی که این جدول را ایجاد می کند، queue:table دستور Artisan را اجرا کنید. پس از ایجاد مهاجرت، می توانید پایگاه داده خود را با استفاده از migrate دستور مهاجرت کنید:

php artisan queue:table
 
php artisan migrate

در نهایت، فراموش نکنید که database با به روز رسانی QUEUE_CONNECTION متغیر موجود در فایل برنامه، به برنامه خود دستور استفاده از درایور را بدهید .env :

QUEUE_CONNECTION=database

ردیس

برای استفاده از redis درایور صف، باید یک اتصال پایگاه داده Redis را در config/database.php فایل پیکربندی خود پیکربندی کنید.

خوشه ردیس

اگر اتصال صف Redis شما از یک Redis Cluster استفاده می‌کند، نام‌های صف شما باید حاوی تگ هش کلید باشند . این برای اطمینان از اینکه همه کلیدهای Redis برای یک صف معین در یک شکاف هش قرار می‌گیرند، لازم است:

'redis' => [
'driver' => 'redis',
'connection' => 'default',
'queue' => '{default}',
'retry_after' => 90,
],

مسدود کردن

هنگام استفاده از صف Redis، می‌توانید از block_for گزینه پیکربندی استفاده کنید تا مشخص کنید راننده چه مدت باید منتظر بماند تا یک کار قبل از تکرار از طریق حلقه کارگر و نظرسنجی مجدد پایگاه داده Redis باشد.

تنظیم این مقدار بر اساس بار صف می تواند کارآمدتر از نظرسنجی مداوم پایگاه داده Redis برای کارهای جدید باشد. به عنوان مثال، می توانید مقدار را 5 برای نشان دادن اینکه درایور باید برای پنج ثانیه مسدود شود در حالی که منتظر در دسترس قرار گرفتن یک کار است، تنظیم کنید:

'redis' => [
'driver' => 'redis',
'connection' => 'default',
'queue' => 'default',
'retry_after' => 90,
'block_for' => 5,
],

تنظیم block_for روی 0 باعث می‌شود تا کارگران صف به‌طور نامحدود مسدود شوند تا زمانی که کار در دسترس باشد. این همچنین از کنترل سیگنال‌هایی مانند SIGTERM تا پردازش کار بعدی جلوگیری می‌کند.

سایر پیش نیازهای راننده

وابستگی های زیر برای درایورهای صف فهرست شده مورد نیاز است. این وابستگی ها ممکن است از طریق مدیر بسته Composer نصب شوند:

  • آمازون SQS: aws/aws-sdk-php ~3.0
  • Beanstalkd: pda/pheanstalk ~4.0
  • Redis: predis/predis ~1.0 یا پسوند phpredis PHP

ایجاد اشتغال

ایجاد کلاس های شغلی

به‌طور پیش‌فرض، تمام کارهای قابل صف برای برنامه شما در app/Jobs فهرست ذخیره می‌شوند. اگر app/Jobs دایرکتوری وجود نداشته باشد، با اجرای make:job دستور Artisan ایجاد می شود:

php artisan make:job ProcessPodcast

کلاس تولید شده Illuminate\Contracts\Queue\ShouldQueue اینترفیس را پیاده‌سازی می‌کند و به لاراول نشان می‌دهد که کار باید به صف فشار داده شود تا به صورت ناهمزمان اجرا شود.

می توان با استفاده از انتشار خرد کار سفارشی کرد .

ساختار کلاس

کلاس‌های کار بسیار ساده هستند، معمولاً فقط شامل یک handle متد هستند که زمانی که کار توسط صف پردازش می‌شود فراخوانی می‌شود. برای شروع، بیایید نگاهی به یک کلاس شغلی بیاندازیم. در این مثال، وانمود می‌کنیم که یک سرویس انتشار پادکست را مدیریت می‌کنیم و باید فایل‌های پادکست آپلود شده را قبل از انتشار پردازش کنیم:

<?php
 
namespace App\Jobs;
 
use App\Models\Podcast;
use App\Services\AudioProcessor;
use Illuminate\Bus\Queueable;
use Illuminate\Contracts\Queue\ShouldQueue;
use Illuminate\Foundation\Bus\Dispatchable;
use Illuminate\Queue\InteractsWithQueue;
use Illuminate\Queue\SerializesModels;
 
class ProcessPodcast implements ShouldQueue
{
use Dispatchable, InteractsWithQueue, Queueable, SerializesModels;
 
/**
* The podcast instance.
*
* @var \App\Models\Podcast
*/
public $podcast;
 
/**
* Create a new job instance.
*
* @param App\Models\Podcast $podcast
* @return void
*/
public function __construct(Podcast $podcast)
{
$this->podcast = $podcast;
}
 
/**
* Execute the job.
*
* @param App\Services\AudioProcessor $processor
* @return void
*/
public function handle(AudioProcessor $processor)
{
// Process uploaded podcast...
}
}

در این مثال، توجه داشته باشید که ما توانستیم یک مدل Eloquent را مستقیماً به سازنده کار در صف ارسال کنیم. به دلیل SerializesModels ویژگی‌ای که کار از آن استفاده می‌کند، مدل‌های Eloquent و روابط بارگذاری شده آن‌ها به‌خوبی سریال‌سازی می‌شوند و در هنگام پردازش کار، سریال‌سازی نمی‌شوند.

اگر کار در صف شما یک مدل Eloquent را در سازنده خود بپذیرد، فقط شناسه مدل در صف سریال قرار می گیرد. هنگامی که کار واقعاً انجام می شود، سیستم صف به طور خودکار نمونه کامل مدل و روابط بارگذاری شده آن را از پایگاه داده بازیابی می کند. این رویکرد به سریال سازی مدل اجازه می دهد تا محموله های کار بسیار کوچکتری به راننده صف شما ارسال شود.

handle روش تزریق وابستگی

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

اگر می‌خواهید کنترل کاملی بر نحوه تزریق وابستگی‌های کانتینر به handle روش در دست بگیرید، می‌توانید از روش ظرف استفاده کنید bindMethod . متد bindMethod یک callback را می پذیرد که کار و کانتینر را دریافت می کند. در بازخوانی، شما آزاد هستید که handle روش را هر طور که می خواهید فراخوانی کنید. به طور معمول، شما باید این روش را از روش ارائه دهنده خدمات boot خود فراخوانی کنید : App\Providers\AppServiceProvider

use App\Jobs\ProcessPodcast;
use App\Services\AudioProcessor;
 
$this->app->bindMethod([ProcessPodcast::class, 'handle'], function ($job, $app) {
return $job->handle($app->make(AudioProcessor::class));
});

داده های باینری، مانند محتویات تصویر خام، باید base64_encode قبل از ارسال به یک کار در صف، از طریق تابع ارسال شوند. در غیر این صورت، کار ممکن است به درستی به JSON تبدیل نشود، زمانی که در صف قرار می‌گیرد.

روابط در صف

از آنجایی که روابط بارگذاری شده نیز سریالی می شوند، رشته شغلی سریالی می تواند گاهی اوقات بسیار بزرگ شود. برای جلوگیری از سریال‌سازی روابط، می‌توانید withoutRelations هنگام تنظیم مقدار ویژگی، متد را روی مدل فراخوانی کنید. این روش نمونه ای از مدل را بدون روابط بارگذاری شده آن برمی گرداند:

/**
* Create a new job instance.
*
* @param \App\Models\Podcast $podcast
* @return void
*/
public function __construct(Podcast $podcast)
{
$this->podcast = $podcast->withoutRelations();
}

علاوه بر این، هنگامی که یک کار از حالت سریال خارج می شود و روابط مدل مجدداً از پایگاه داده بازیابی می شوند، به طور کامل بازیابی می شوند. هر گونه محدودیت رابطه قبلی که قبل از سریال سازی مدل در طول فرآیند صف کار اعمال شده بود، در زمانی که کار غیراصولی شود، اعمال نخواهد شد. بنابراین، اگر می‌خواهید با زیرمجموعه‌ای از یک رابطه معین کار کنید، باید دوباره آن رابطه را در شغل صف خود محدود کنید.

مشاغل منحصر به فرد

مشاغل منحصر به فرد نیاز به یک درایور حافظه پنهان دارند که از قفل ها پشتیبانی می کند . در حال حاضر، درایورهای memcached , redis , dynamodb , database , file و array کش از قفل اتمی پشتیبانی می کنند. علاوه بر این، محدودیت‌های شغلی منحصربه‌فرد برای مشاغل درون دسته‌ای اعمال نمی‌شود.

گاهی اوقات، ممکن است بخواهید مطمئن شوید که تنها یک نمونه از یک کار خاص در هر نقطه از زمان در صف است. می توانید این کار را با پیاده سازی ShouldBeUnique رابط در کلاس شغلی خود انجام دهید. این رابط نیازی به تعریف هیچ روش اضافی در کلاس خود ندارد:

<?php
 
use Illuminate\Contracts\Queue\ShouldQueue;
use Illuminate\Contracts\Queue\ShouldBeUnique;
 
class UpdateSearchIndex implements ShouldQueue, ShouldBeUnique
{
...
}

در مثال بالا، UpdateSearchIndex کار منحصر به فرد است. بنابراین، اگر نمونه دیگری از کار از قبل در صف باشد و پردازش تمام نشده باشد، کار ارسال نخواهد شد.

در موارد خاص، ممکن است بخواهید «کلید» خاصی را تعریف کنید که کار را منحصربه‌فرد می‌کند یا ممکن است بخواهید زمانی را مشخص کنید که پس از آن کار دیگر منحصربه‌فرد باقی نمی‌ماند. برای انجام این کار، می‌توانید ویژگی‌ها یا روش‌هایی را در کلاس شغلی خود تعریف uniqueId کنید uniqueFor :

<?php
 
use App\Models\Product;
use Illuminate\Contracts\Queue\ShouldQueue;
use Illuminate\Contracts\Queue\ShouldBeUnique;
 
class UpdateSearchIndex implements ShouldQueue, ShouldBeUnique
{
/**
* The product instance.
*
* @var \App\Product
*/
public $product;
 
/**
* The number of seconds after which the job's unique lock will be released.
*
* @var int
*/
public $uniqueFor = 3600;
 
/**
* The unique ID of the job.
*
* @return string
*/
public function uniqueId()
{
return $this->product->id;
}
}

در مثال بالا، UpdateSearchIndex کار با شناسه محصول منحصر به فرد است. بنابراین، هر گونه ارسال جدید از کار با همان شناسه محصول نادیده گرفته می شود تا زمانی که کار موجود پردازش را تکمیل کند. علاوه بر این، اگر کار موجود ظرف یک ساعت پردازش نشود، قفل منحصربه‌فرد آزاد می‌شود و کار دیگری با همان کلید یکتا به صف ارسال می‌شود.

اگر برنامه شما کارها را از چندین وب سرور یا کانتینر ارسال می کند، باید اطمینان حاصل کنید که همه سرورهای شما با یک سرور کش مرکزی یکسان در ارتباط هستند تا لاراول بتواند به طور دقیق تشخیص دهد که آیا یک کار منحصر به فرد است یا خیر.

منحصر به فرد نگه داشتن مشاغل تا زمان شروع پردازش

به‌طور پیش‌فرض، مشاغل منحصربه‌فرد پس از تکمیل پردازش یا شکست تمام تلاش‌های مجدد آن، «باز» می‌شوند. با این حال، ممکن است شرایطی وجود داشته باشد که بخواهید قفل کارتان بلافاصله قبل از پردازش آن باز شود. برای انجام این کار، شغل شما باید ShouldBeUniqueUntilProcessing به جای ShouldBeUnique قرارداد، قرارداد را اجرا کند:

<?php
 
use App\Models\Product;
use Illuminate\Contracts\Queue\ShouldQueue;
use Illuminate\Contracts\Queue\ShouldBeUniqueUntilProcessing;
 
class UpdateSearchIndex implements ShouldQueue, ShouldBeUniqueUntilProcessing
{
// ...
}

قفل های شغلی منحصر به فرد

در پشت صحنه، هنگامی که یک ShouldBeUnique کار ارسال می شود، لاراول تلاش می کند تا یک قفل را با uniqueId کلید بدست آورد. اگر قفل به دست نیامد، کار ارسال نمی شود. این قفل زمانی آزاد می شود که کار پردازش کامل شود یا تمام تلاش های مجدد آن با شکست مواجه شود. به طور پیش فرض، لاراول از درایور کش پیش فرض برای به دست آوردن این قفل استفاده می کند. با این حال، اگر می‌خواهید از درایور دیگری برای به دست آوردن قفل استفاده کنید، می‌توانید روشی را تعریف کنید uniqueVia که درایور کش را که باید استفاده شود برمی‌گرداند:

use Illuminate\Support\Facades\Cache;
 
class UpdateSearchIndex implements ShouldQueue, ShouldBeUnique
{
...
 
/**
* Get the cache driver for the unique job lock.
*
* @return \Illuminate\Contracts\Cache\Repository
*/
public function uniqueVia()
{
return Cache::driver('redis');
}
}

اگر فقط باید پردازش همزمان یک کار را محدود کنید، WithoutOverlapping به جای آن از میان افزار کار استفاده کنید.

میان افزار شغلی

میان‌افزارهای شغلی به شما این امکان را می‌دهند که منطق سفارشی را حول اجرای کارهای در صف بپیچید، و باعث کاهش حجم دیگ در خود کارها می‌شود. به عنوان مثال، روش زیر را در نظر بگیرید handle که از ویژگی‌های محدودکننده نرخ Redis لاراول استفاده می‌کند تا فقط یک کار را هر پنج ثانیه پردازش کند:

use Illuminate\Support\Facades\Redis;
 
/**
* Execute the job.
*
* @return void
*/
public function handle()
{
Redis::throttle('key')->block(0)->allow(1)->every(5)->then(function () {
info('Lock obtained...');
 
// Handle job...
}, function () {
// Could not obtain lock...
 
return $this->release(5);
});
}

در حالی که این کد معتبر است، پیاده سازی روش handle نویزدار می شود زیرا با منطق محدود کننده نرخ Redis درهم است. علاوه بر این، این منطق محدود کننده نرخ باید برای هر شغل دیگری که می‌خواهیم رتبه‌بندی کنیم، تکرار شود.

به جای محدود کردن نرخ در روش دسته، می‌توانیم یک میان‌افزار شغلی تعریف کنیم که محدودیت نرخ را مدیریت می‌کند. لاراول مکان پیش‌فرضی برای میان‌افزار شغلی ندارد، بنابراین می‌توانید میان‌افزار شغلی را در هر جایی از برنامه خود قرار دهید. در این مثال، میان افزار را در یک دایرکتوری قرار می دهیم app/Jobs/Middleware :

<?php
 
namespace App\Jobs\Middleware;
 
use Illuminate\Support\Facades\Redis;
 
class RateLimited
{
/**
* Process the queued job.
*
* @param mixed $job
* @param callable $next
* @return mixed
*/
public function handle($job, $next)
{
Redis::throttle('key')
->block(0)->allow(1)->every(5)
->then(function () use ($job, $next) {
// Lock obtained...
 
$next($job);
}, function () use ($job) {
// Could not obtain lock...
 
$job->release(5);
});
}
}

همانطور که می‌بینید، مانند میان‌افزار مسیر ، میان‌افزار شغلی کار در حال پردازش را دریافت می‌کند و یک فراخوانی که باید برای ادامه پردازش کار فراخوانی شود.

پس از ایجاد میان‌افزار شغل، ممکن است با بازگرداندن آنها از روش کار، به یک شغل متصل شوند middleware . این روش در مشاغل دارای داربست با دستور Artisan وجود ندارد make:job ، بنابراین باید به صورت دستی آن را به کلاس شغلی خود اضافه کنید:

use App\Jobs\Middleware\RateLimited;
 
/**
* Get the middleware the job should pass through.
*
* @return array
*/
public function middleware()
{
return [new RateLimited];
}

میان‌افزار شغلی را می‌توان به شنونده‌های رویداد، پیام‌های پستی و اعلان‌ها نیز اختصاص داد.

محدود کردن نرخ

اگرچه ما به تازگی نشان دادیم که چگونه می توان میان افزار شغلی محدود کننده نرخ خود را بنویسید، لاراول در واقع شامل یک میان افزار محدود کننده نرخ است که ممکن است برای رتبه بندی مشاغل محدود از آن استفاده کنید. مانند محدود کننده های نرخ مسیر ، محدود کننده های نرخ کار با استفاده از روش RateLimiter نما تعریف می شوند for .

به عنوان مثال، ممکن است بخواهید به کاربران اجازه دهید یک بار در ساعت از داده های خود نسخه پشتیبان تهیه کنند، در حالی که چنین محدودیتی برای مشتریان ممتاز اعمال نمی شود. برای انجام این کار، می توانید a را RateLimiter در boot متد خود تعریف کنید AppServiceProvider :

use Illuminate\Cache\RateLimiting\Limit;
use Illuminate\Support\Facades\RateLimiter;
 
/**
* Bootstrap any application services.
*
* @return void
*/
public function boot()
{
RateLimiter::for('backups', function ($job) {
return $job->user->vipCustomer()
? Limit::none()
: Limit::perHour(1)->by($job->user->id);
});
}

در مثال بالا، ما محدودیت نرخ ساعتی را تعریف کردیم. با این حال، می توانید به راحتی با استفاده از perMinute روش، محدودیت نرخ را بر اساس دقیقه تعریف کنید. علاوه بر این، می‌توانید هر مقداری را که می‌خواهید به by روش محدودیت نرخ ارسال کنید. با این حال، این مقدار اغلب برای بخش بندی محدودیت های نرخ توسط مشتری استفاده می شود:

return Limit::perMinute(50)->by($job->user->id);

هنگامی که محدودیت نرخ خود را تعیین کردید، می توانید با استفاده از Illuminate\Queue\Middleware\RateLimited میان افزار، محدود کننده نرخ را به کار پشتیبان خود متصل کنید. هر بار که کار از حد مجاز تجاوز می کند، این میان افزار کار را با تاخیر مناسب بر اساس مدت زمان محدودیت نرخ، به صف باز می گرداند.

use Illuminate\Queue\Middleware\RateLimited;
 
/**
* Get the middleware the job should pass through.
*
* @return array
*/
public function middleware()
{
return [new RateLimited('backups')];
}

بازگرداندن یک کار با نرخ محدود دوباره در صف، تعداد کل کار را افزایش می دهد attempts . ممکن است بخواهید بر اساس آن، ویژگی های خود tries و maxExceptions طبقه شغلی خود را تنظیم کنید. یا، ممکن است بخواهید از این retryUntil روش برای تعیین مدت زمانی استفاده کنید که کار دیگر نباید انجام شود.

اگر نمی خواهید یک کار زمانی که دارای نرخ محدود است دوباره امتحان شود، می توانید از dontRelease روش زیر استفاده کنید:

/**
* Get the middleware the job should pass through.
*
* @return array
*/
public function middleware()
{
return [(new RateLimited('backups'))->dontRelease()];
}

اگر از Redis استفاده می‌کنید، می‌توانید از Illuminate\Queue\Middleware\RateLimitedWithRedis میان‌افزار استفاده کنید که برای Redis به‌خوبی تنظیم شده است و از میان‌افزار محدودکننده نرخ پایه کارآمدتر است.

جلوگیری از همپوشانی مشاغل

لاراول شامل یک Illuminate\Queue\Middleware\WithoutOverlapping میان افزار است که به شما امکان می دهد از همپوشانی کار بر اساس یک کلید دلخواه جلوگیری کنید. این می تواند مفید باشد زمانی که یک کار در صف در حال تغییر منبعی است که فقط باید توسط یک کار در یک زمان اصلاح شود.

به عنوان مثال، بیایید تصور کنیم که یک کار در صف دارید که امتیاز اعتبار یک کاربر را به روز می کند و می خواهید از همپوشانی کار به روز رسانی امتیاز اعتبار برای همان شناسه کاربری جلوگیری کنید. برای انجام این کار، می توانید WithoutOverlapping میان افزار را از روش شغل خود برگردانید middleware :

use Illuminate\Queue\Middleware\WithoutOverlapping;
 
/**
* Get the middleware the job should pass through.
*
* @return array
*/
public function middleware()
{
return [new WithoutOverlapping($this->user->id)];
}

هر گونه کار همپوشانی از همان نوع به صف بازگردانده می شود. همچنین می‌توانید تعداد ثانیه‌هایی را که باید قبل از انجام دوباره کار آزاد شده سپری شود را مشخص کنید:

/**
* Get the middleware the job should pass through.
*
* @return array
*/
public function middleware()
{
return [(new WithoutOverlapping($this->order->id))->releaseAfter(60)];
}

اگر می‌خواهید فوراً کارهای همپوشانی را حذف کنید تا دوباره امتحان نشوند، می‌توانید از dontRelease روش زیر استفاده کنید:

/**
* Get the middleware the job should pass through.
*
* @return array
*/
public function middleware()
{
return [(new WithoutOverlapping($this->order->id))->dontRelease()];
}

میان WithoutOverlapping افزار از ویژگی قفل اتمی لاراول تغذیه می شود. گاهی اوقات، کار شما ممکن است به طور غیرمنتظره ای از کار بیفتد یا به گونه ای به پایان برسد که قفل باز نشود. بنابراین، می توانید با استفاده از روش، زمان انقضای قفل را به صراحت تعریف کنید expireAfter . به عنوان مثال، مثال زیر به لاراول دستور می دهد که WithoutOverlapping قفل را سه دقیقه پس از شروع پردازش کار آزاد کند:

/**
* Get the middleware the job should pass through.
*
* @return array
*/
public function middleware()
{
return [(new WithoutOverlapping($this->order->id))->expireAfter(180)];
}

میان WithoutOverlapping افزار به یک درایور کش نیاز دارد که از قفل ها پشتیبانی می کند . در حال حاضر، درایورهای memcached , redis , dynamodb , database , file و array کش از قفل اتمی پشتیبانی می کنند.

به اشتراک گذاری کلیدهای قفل در کلاس های شغلی

به‌طور پیش‌فرض، WithoutOverlapping میان‌افزار فقط از همپوشانی مشاغل هم‌رده جلوگیری می‌کند. بنابراین، اگرچه ممکن است دو کلاس شغلی مختلف از یک کلید قفل استفاده کنند، اما از همپوشانی آنها جلوگیری نخواهد شد. با این حال، می‌توانید به لاراول دستور دهید تا کلید را در کلاس‌های شغلی با استفاده از shared روش زیر اعمال کند:

use Illuminate\Queue\Middleware\WithoutOverlapping;
 
class ProviderIsDown
{
// ...
 
 
public function middleware()
{
return [
(new WithoutOverlapping("status:{$this->provider}"))->shared(),
];
}
}
 
class ProviderIsUp
{
// ...
 
 
public function middleware()
{
return [
(new WithoutOverlapping("status:{$this->provider}"))->shared(),
];
}
}

استثنائات تروتتلینگ

لاراول شامل یک Illuminate\Queue\Middleware\ThrottlesExceptions میان افزار است که به شما امکان می دهد استثنائات را کاهش دهید. هنگامی که کار تعداد معینی از استثناها را ایجاد می کند، تمام تلاش های بعدی برای اجرای کار تا زمانی که یک بازه زمانی مشخص سپری شود به تعویق می افتد. این میان افزار به ویژه برای مشاغلی که با سرویس های شخص ثالث که ناپایدار هستند در تعامل هستند مفید است.

به عنوان مثال، بیایید یک کار در صف را تصور کنیم که با یک API شخص ثالث تعامل دارد که شروع به ایجاد استثنا می کند. برای کاهش دریچه گاز، می توانید ThrottlesExceptions میان افزار را از روش کار خود برگردانید middleware . به طور معمول، این میان افزار باید با کاری که تلاش های مبتنی بر زمان را اجرا می کند جفت شود :

use Illuminate\Queue\Middleware\ThrottlesExceptions;
 
/**
* Get the middleware the job should pass through.
*
* @return array
*/
public function middleware()
{
return [new ThrottlesExceptions(10, 5)];
}
 
/**
* Determine the time at which the job should timeout.
*
* @return \DateTime
*/
public function retryUntil()
{
return now()->addMinutes(5);
}

اولین آرگومان سازنده پذیرفته شده توسط میان‌افزار تعداد استثناهایی است که کار می‌تواند قبل از throttle شدن ایجاد کند، در حالی که آرگومان سازنده دوم تعداد دقیقه‌هایی است که باید قبل از انجام دوباره کار پس از پایان دادن به آن بگذرد. در مثال کد بالا، اگر کار 10 استثنا را در عرض 5 دقیقه پرتاب کند، 5 دقیقه قبل از تلاش مجدد کار صبر می کنیم.

هنگامی که یک کار یک استثنا ایجاد می کند اما آستانه استثنا هنوز به آن نرسیده است، معمولاً کار بلافاصله دوباره امتحان می شود. با این حال، می‌توانید با فراخوانی backoff روش هنگام اتصال میان‌افزار به کار، تعداد دقیقه‌هایی که چنین کاری باید به تأخیر بیفتد را مشخص کنید :

use Illuminate\Queue\Middleware\ThrottlesExceptions;
 
/**
* Get the middleware the job should pass through.
*
* @return array
*/
public function middleware()
{
return [(new ThrottlesExceptions(10, 5))->backoff(5)];
}

در داخل، این میان‌افزار از سیستم کش لاراول برای اجرای محدود کردن نرخ استفاده می‌کند و نام کلاس کار به عنوان «کلید» حافظه پنهان استفاده می‌شود. می توانید با فراخوانی by روش هنگام اتصال میان افزار به شغل خود، این کلید را لغو کنید. این ممکن است مفید باشد اگر چندین شغل دارید که با یک سرویس شخص ثالث در حال تعامل هستند و دوست دارید آنها یک "سطل گاز" مشترک را به اشتراک بگذارند:

use Illuminate\Queue\Middleware\ThrottlesExceptions;
 
/**
* Get the middleware the job should pass through.
*
* @return array
*/
public function middleware()
{
return [(new ThrottlesExceptions(10, 10))->by('key')];
}

اگر از Redis استفاده می‌کنید، می‌توانید از Illuminate\Queue\Middleware\ThrottlesExceptionsWithRedis میان‌افزار استفاده کنید، که برای Redis به‌خوبی تنظیم شده است و از میان‌افزار استثنایی اصلی کارآمدتر است.

اعزام مشاغل

هنگامی که کلاس شغلی خود را نوشتید، می توانید آن را با استفاده از dispatch روش خود کار ارسال کنید. آرگومان های ارسال شده به dispatch متد به سازنده کار داده می شود:

<?php
 
namespace App\Http\Controllers;
 
use App\Http\Controllers\Controller;
use App\Jobs\ProcessPodcast;
use App\Models\Podcast;
use Illuminate\Http\Request;
 
class PodcastController extends Controller
{
/**
* Store a new podcast.
*
* @param \Illuminate\Http\Request $request
* @return \Illuminate\Http\Response
*/
public function store(Request $request)
{
$podcast = Podcast::create(/* ... */);
 
// ...
 
ProcessPodcast::dispatch($podcast);
}
}

اگر می خواهید کاری را به صورت مشروط ارسال کنید، می توانید از روش های dispatchIf زیر استفاده کنید dispatchUnless :

ProcessPodcast::dispatchIf($accountActive, $podcast);
 
ProcessPodcast::dispatchUnless($accountSuspended, $podcast);

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

ارسال با تاخیر

اگر می خواهید مشخص کنید که یک کار نباید فوراً برای پردازش توسط یک کارگر صف در دسترس باشد، می توانید از این delay روش هنگام ارسال کار استفاده کنید. به عنوان مثال، اجازه دهید مشخص کنیم که یک کار نباید تا 10 دقیقه پس از ارسال آن برای پردازش در دسترس باشد:

<?php
 
namespace App\Http\Controllers;
 
use App\Http\Controllers\Controller;
use App\Jobs\ProcessPodcast;
use App\Models\Podcast;
use Illuminate\Http\Request;
 
class PodcastController extends Controller
{
/**
* Store a new podcast.
*
* @param \Illuminate\Http\Request $request
* @return \Illuminate\Http\Response
*/
public function store(Request $request)
{
$podcast = Podcast::create(/* ... */);
 
// ...
 
ProcessPodcast::dispatch($podcast)
->delay(now()->addMinutes(10));
}
}

سرویس صف آمازون SQS دارای حداکثر زمان تاخیر 15 دقیقه است.

ارسال پس از ارسال پاسخ به مرورگر

از طرف دیگر، اگر سرور وب شما از FastCGI استفاده می کند، این dispatchAfterResponse روش ارسال یک کار را تا زمانی که پاسخ HTTP به مرورگر کاربر ارسال شود به تأخیر می اندازد. این همچنان به کاربر اجازه می دهد تا استفاده از برنامه را شروع کند، حتی اگر یک کار در صف هنوز در حال اجرا باشد. این معمولاً باید فقط برای کارهایی استفاده شود که حدود یک ثانیه طول می کشد، مانند ارسال ایمیل. از آنجایی که آنها در درخواست فعلی HTTP پردازش می شوند، کارهایی که به این روش ارسال می شوند نیازی به اجرای صف کارگر ندارند تا پردازش شوند:

use App\Jobs\SendNotification;
 
SendNotification::dispatchAfterResponse();

همچنین می‌توانید روش را dispatch ببندید و afterResponse به dispatch کمک زنجیر کنید تا پس از ارسال پاسخ HTTP به مرورگر، بسته شدن را اجرا کنید:

use App\Mail\WelcomeMessage;
use Illuminate\Support\Facades\Mail;
 
dispatch(function () {
Mail::to('taylor@example.com')->send(new WelcomeMessage);
})->afterResponse();

دیسپاچینگ همزمان

اگر می خواهید یک کار را فوراً (به طور همزمان) ارسال کنید، می توانید از این dispatchSync روش استفاده کنید. هنگام استفاده از این روش، کار در صف قرار نمی گیرد و بلافاصله در فرآیند فعلی اجرا می شود:

<?php
 
namespace App\Http\Controllers;
 
use App\Http\Controllers\Controller;
use App\Jobs\ProcessPodcast;
use App\Models\Podcast;
use Illuminate\Http\Request;
 
class PodcastController extends Controller
{
/**
* Store a new podcast.
*
* @param \Illuminate\Http\Request $request
* @return \Illuminate\Http\Response
*/
public function store(Request $request)
{
$podcast = Podcast::create(/* ... */);
 
// Create podcast...
 
ProcessPodcast::dispatchSync($podcast);
}
}

مشاغل و معاملات پایگاه داده

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

Thankfully, Laravel provides several methods of working around this problem. First, you may set the after_commit connection option in your queue connection's configuration array:

'redis' => [
'driver' => 'redis',
// ...
'after_commit' => true,
],

When the after_commit option is true, you may dispatch jobs within database transactions; however, Laravel will wait until the open parent database transactions have been committed before actually dispatching the job. Of course, if no database transactions are currently open, the job will be dispatched immediately.

If a transaction is rolled back due to an exception that occurs during the transaction, the jobs that were dispatched during that transaction will be discarded.

Setting the after_commit configuration option to true will also cause any queued event listeners, mailables, notifications, and broadcast events to be dispatched after all open database transactions have been committed.

Specifying Commit Dispatch Behavior Inline

If you do not set the after_commit queue connection configuration option to true, you may still indicate that a specific job should be dispatched after all open database transactions have been committed. To accomplish this, you may chain the afterCommit method onto your dispatch operation:

use App\Jobs\ProcessPodcast;
 
ProcessPodcast::dispatch($podcast)->afterCommit();

Likewise, if the after_commit configuration option is set to true, you may indicate that a specific job should be dispatched immediately without waiting for any open database transactions to commit:

ProcessPodcast::dispatch($podcast)->beforeCommit();

Job Chaining

Job chaining allows you to specify a list of queued jobs that should be run in sequence after the primary job has executed successfully. If one job in the sequence fails, the rest of the jobs will not be run. To execute a queued job chain, you may use the chain method provided by the Bus facade. Laravel's command bus is a lower level component that queued job dispatching is built on top of:

use App\Jobs\OptimizePodcast;
use App\Jobs\ProcessPodcast;
use App\Jobs\ReleasePodcast;
use Illuminate\Support\Facades\Bus;
 
Bus::chain([
new ProcessPodcast,
new OptimizePodcast,
new ReleasePodcast,
])->dispatch();

In addition to chaining job class instances, you may also chain closures:

Bus::chain([
new ProcessPodcast,
new OptimizePodcast,
function () {
Podcast::update(/* ... */);
},
])->dispatch();

Deleting jobs using the $this->delete() method within the job will not prevent chained jobs from being processed. The chain will only stop executing if a job in the chain fails.

Chain Connection & Queue

If you would like to specify the connection and queue that should be used for the chained jobs, you may use the onConnection and onQueue methods. These methods specify the queue connection and queue name that should be used unless the queued job is explicitly assigned a different connection / queue:

Bus::chain([
new ProcessPodcast,
new OptimizePodcast,
new ReleasePodcast,
])->onConnection('redis')->onQueue('podcasts')->dispatch();

Chain Failures

When chaining jobs, you may use the catch method to specify a closure that should be invoked if a job within the chain fails. The given callback will receive the Throwable instance that caused the job failure:

use Illuminate\Support\Facades\Bus;
use Throwable;
 
Bus::chain([
new ProcessPodcast,
new OptimizePodcast,
new ReleasePodcast,
])->catch(function (Throwable $e) {
// A job within the chain has failed...
})->dispatch();

Since chain callbacks are serialized and executed at a later time by the Laravel queue, you should not use the $this variable within chain callbacks.

Customizing The Queue & Connection

Dispatching To A Particular Queue

با فشار دادن مشاغل به صف های مختلف، ممکن است مشاغل در صف خود را "دسته بندی" کنید و حتی تعداد کارگرانی را که به صف های مختلف اختصاص می دهید، اولویت بندی کنید. به خاطر داشته باشید، این کارها را به سمت "اتصالات" صف مختلف که توسط فایل پیکربندی صف شما تعریف شده است، سوق نمی دهد، بلکه فقط به صف های خاص در یک اتصال واحد می رسد. برای تعیین صف، onQueue هنگام ارسال کار از روش استفاده کنید:

<?php
 
namespace App\Http\Controllers;
 
use App\Http\Controllers\Controller;
use App\Jobs\ProcessPodcast;
use App\Models\Podcast;
use Illuminate\Http\Request;
 
class PodcastController extends Controller
{
/**
* Store a new podcast.
*
* @param \Illuminate\Http\Request $request
* @return \Illuminate\Http\Response
*/
public function store(Request $request)
{
$podcast = Podcast::create(/* ... */);
 
// Create podcast...
 
ProcessPodcast::dispatch($podcast)->onQueue('processing');
}
}

از طرف دیگر، می‌توانید صف شغل را با فراخوانی onQueue متد در سازنده کار مشخص کنید:

<?php
 
namespace App\Jobs;
 
use Illuminate\Bus\Queueable;
use Illuminate\Contracts\Queue\ShouldQueue;
use Illuminate\Foundation\Bus\Dispatchable;
use Illuminate\Queue\InteractsWithQueue;
use Illuminate\Queue\SerializesModels;
 
class ProcessPodcast implements ShouldQueue
{
use Dispatchable, InteractsWithQueue, Queueable, SerializesModels;
 
/**
* Create a new job instance.
*
* @return void
*/
public function __construct()
{
$this->onQueue('processing');
}
}

ارسال به یک اتصال خاص

اگر برنامه شما با چندین اتصال صف تعامل دارد، می‌توانید با استفاده از onConnection روش مشخص کنید که کدام اتصال را به یک کار فشار دهید:

<?php
 
namespace App\Http\Controllers;
 
use App\Http\Controllers\Controller;
use App\Jobs\ProcessPodcast;
use App\Models\Podcast;
use Illuminate\Http\Request;
 
class PodcastController extends Controller
{
/**
* Store a new podcast.
*
* @param \Illuminate\Http\Request $request
* @return \Illuminate\Http\Response
*/
public function store(Request $request)
{
$podcast = Podcast::create(/* ... */);
 
// Create podcast...
 
ProcessPodcast::dispatch($podcast)->onConnection('sqs');
}
}

می‌توانید برای تعیین اتصال و صف برای یک کار، onConnection و متدها را به هم متصل کنید: onQueue

ProcessPodcast::dispatch($podcast)
->onConnection('sqs')
->onQueue('processing');

onConnection همچنین، می‌توانید با فراخوانی متد در سازنده کار، اتصال کار را مشخص کنید :

<?php
 
namespace App\Jobs;
 
use Illuminate\Bus\Queueable;
use Illuminate\Contracts\Queue\ShouldQueue;
use Illuminate\Foundation\Bus\Dispatchable;
use Illuminate\Queue\InteractsWithQueue;
use Illuminate\Queue\SerializesModels;
 
class ProcessPodcast implements ShouldQueue
{
use Dispatchable, InteractsWithQueue, Queueable, SerializesModels;
 
/**
* Create a new job instance.
*
* @return void
*/
public function __construct()
{
$this->onConnection('sqs');
}
}

تعیین حداکثر تلاش‌های شغلی / مقادیر زمان‌بندی

حداکثر تلاش

اگر یکی از مشاغل در صف شما با خطا مواجه می شود، احتمالاً نمی خواهید آن را به طور نامحدود دوباره امتحان کنید. بنابراین، لاراول راه های مختلفی برای تعیین چند بار یا مدت زمان انجام یک کار ارائه می دهد.

یک روش برای تعیین حداکثر تعداد دفعاتی که ممکن است یک کار انجام شود، از طریق --tries سوئیچ در خط فرمان Artisan است. این برای تمام کارهایی که کارگر پردازش می‌کند اعمال می‌شود، مگر اینکه کار در حال پردازش تعداد دفعات انجام آن را مشخص کند:

php artisan queue:work --tries=3

اگر یک کار از حداکثر تعداد تلاش خود بیشتر شود، به عنوان یک کار "شکست خورده" در نظر گرفته می شود. برای اطلاعات بیشتر در مورد رسیدگی به مشاغل ناموفق، به مستندات شغلی ناموفق مراجعه کنید . اگر --tries=0 به دستور ارائه شود queue:work ، کار به طور نامحدود دوباره امتحان می شود.

شما می توانید با تعیین حداکثر تعداد دفعاتی که ممکن است یک کار در خود طبقه شغلی انجام شود، رویکرد دقیق تری داشته باشید. اگر حداکثر تعداد تلاش در کار مشخص شده باشد، بر مقدار --tries ارائه شده در خط فرمان ارجحیت خواهد داشت:

<?php
 
namespace App\Jobs;
 
class ProcessPodcast implements ShouldQueue
{
/**
* The number of times the job may be attempted.
*
* @var int
*/
public $tries = 5;
}

تلاش های مبتنی بر زمان

به‌عنوان جایگزینی برای تعریف چند بار انجام یک کار قبل از شکست، می‌توانید زمانی را تعریف کنید که در آن کار دیگر نباید تلاش کرد. این اجازه می دهد تا یک کار هر تعداد بار در یک بازه زمانی مشخص انجام شود. برای تعیین زمانی که در آن کار دیگر نباید تلاش کرد، روشی را retryUntil به کلاس شغلی خود اضافه کنید. این متد باید یک DateTime نمونه برگرداند :

/**
* Determine the time at which the job should timeout.
*
* @return \DateTime
*/
public function retryUntil()
{
return now()->addMinutes(10);
}

همچنین می توانید یک tries ویژگی یا retryUntil متد را در شنوندگان رویداد در صف خود تعریف کنید .

حداکثر استثنائات

Sometimes you may wish to specify that a job may be attempted many times, but should fail if the retries are triggered by a given number of unhandled exceptions (as opposed to being released by the release method directly). To accomplish this, you may define a maxExceptions property on your job class:

<?php
 
namespace App\Jobs;
 
use Illuminate\Support\Facades\Redis;
 
class ProcessPodcast implements ShouldQueue
{
/**
* The number of times the job may be attempted.
*
* @var int
*/
public $tries = 25;
 
/**
* The maximum number of unhandled exceptions to allow before failing.
*
* @var int
*/
public $maxExceptions = 3;
 
/**
* Execute the job.
*
* @return void
*/
public function handle()
{
Redis::throttle('key')->allow(10)->every(60)->then(function () {
// Lock obtained, process the podcast...
}, function () {
// Unable to obtain lock...
return $this->release(10);
});
}
}

In this example, the job is released for ten seconds if the application is unable to obtain a Redis lock and will continue to be retried up to 25 times. However, the job will fail if three unhandled exceptions are thrown by the job.

Timeout

The pcntl PHP extension must be installed in order to specify job timeouts.

Often, you know roughly how long you expect your queued jobs to take. For this reason, Laravel allows you to specify a "timeout" value. By default, the timeout value is 60 seconds. If a job is processing for longer than the number of seconds specified by the timeout value, the worker processing the job will exit with an error. Typically, the worker will be restarted automatically by a process manager configured on your server.

The maximum number of seconds that jobs can run may be specified using the --timeout switch on the Artisan command line:

php artisan queue:work --timeout=30

If the job exceeds its maximum attempts by continually timing out, it will be marked as failed.

You may also define the maximum number of seconds a job should be allowed to run on the job class itself. If the timeout is specified on the job, it will take precedence over any timeout specified on the command line:

<?php
 
namespace App\Jobs;
 
class ProcessPodcast implements ShouldQueue
{
/**
* The number of seconds the job can run before timing out.
*
* @var int
*/
public $timeout = 120;
}

Sometimes, IO blocking processes such as sockets or outgoing HTTP connections may not respect your specified timeout. Therefore, when using these features, you should always attempt to specify a timeout using their APIs as well. For example, when using Guzzle, you should always specify a connection and request timeout value.

Failing On Timeout

If you would like to indicate that a job should be marked as failed on timeout, you may define the $failOnTimeout property on the job class:

/**
* Indicate if the job should be marked as failed on timeout.
*
* @var bool
*/
public $failOnTimeout = true;

Error Handling

If an exception is thrown while the job is being processed, the job will automatically be released back onto the queue so it may be attempted again. The job will continue to be released until it has been attempted the maximum number of times allowed by your application. The maximum number of attempts is defined by the --tries switch used on the queue:work Artisan command. Alternatively, the maximum number of attempts may be defined on the job class itself. More information on running the queue worker can be found below.

Manually Releasing A Job

Sometimes you may wish to manually release a job back onto the queue so that it can be attempted again at a later time. You may accomplish this by calling the release method:

/**
* Execute the job.
*
* @return void
*/
public function handle()
{
// ...
 
$this->release();
}

به طور پیش‌فرض، این release روش کار را برای پردازش فوری به صف باز می‌گرداند. با این حال، با ارسال یک عدد صحیح به release متد، می‌توانید به صف دستور دهید تا زمانی که تعداد معینی از ثانیه سپری نشده است، کار را برای پردازش در دسترس قرار ندهد:

$this->release(10);

شکست دستی یک کار

گاهی اوقات ممکن است لازم باشد یک کار را به صورت دستی به عنوان "شکست خورده" علامت گذاری کنید. برای انجام این کار، می توانید fail متد را فراخوانی کنید:

/**
* Execute the job.
*
* @return void
*/
public function handle()
{
// ...
 
$this->fail();
}

اگر می‌خواهید شغل خود را به‌عنوان ناموفق علامت‌گذاری کنید، به دلیل استثنایی که پیدا کرده‌اید، می‌توانید استثنا را به fail روش منتقل کنید. یا، برای راحتی، ممکن است یک پیام خطای رشته ای ارسال کنید که برای شما به استثنا تبدیل می شود:

$this->fail($exception);
 
$this->fail('Something went wrong.');

Job Batching

ویژگی Job Batching لاراول به شما این امکان را می‌دهد که به راحتی دسته‌ای از کارها را اجرا کنید و سپس زمانی که دسته‌ای از کارها اجرا شدند، برخی اقدامات را انجام دهید. قبل از شروع، باید یک انتقال پایگاه داده ایجاد کنید تا جدولی بسازید که حاوی اطلاعات متا در مورد دسته های شغلی شما، مانند درصد تکمیل آنها باشد. این مهاجرت ممکن است با استفاده از دستور Artisan ایجاد شود queue:batches-table :

php artisan queue:batches-table
 
php artisan migrate

تعریف مشاغل قابل دسته بندی

برای تعریف یک کار قابل دسته بندی، باید یک کار در صف به طور معمول ایجاد کنید. با این حال، شما باید این Illuminate\Bus\Batchable ویژگی را به کلاس شغلی اضافه کنید. این ویژگی دسترسی به batch روشی را فراهم می کند که ممکن است برای بازیابی دسته فعلی که کار در آن اجرا می شود استفاده شود:

<?php
 
namespace App\Jobs;
 
use Illuminate\Bus\Batchable;
use Illuminate\Bus\Queueable;
use Illuminate\Contracts\Queue\ShouldQueue;
use Illuminate\Foundation\Bus\Dispatchable;
use Illuminate\Queue\InteractsWithQueue;
use Illuminate\Queue\SerializesModels;
 
class ImportCsv implements ShouldQueue
{
use Batchable, Dispatchable, InteractsWithQueue, Queueable, SerializesModels;
 
/**
* Execute the job.
*
* @return void
*/
public function handle()
{
if ($this->batch()->cancelled()) {
// Determine if the batch has been cancelled...
 
return;
}
 
// Import a portion of the CSV file...
}
}

ارسال دسته ها

برای ارسال دسته ای از کارها باید از batch روش نما استفاده کنید Bus . البته، دسته بندی در درجه اول زمانی مفید است که با تماس های تکمیلی ترکیب شود. بنابراین، می‌توانید از روش‌های then ، catch و finally برای تعریف تماس‌های تکمیلی برای دسته استفاده کنید. هر یک از این تماس‌ها Illuminate\Bus\Batch در هنگام فراخوانی یک نمونه دریافت می‌کنند . در این مثال، تصور می کنیم که در صف دسته ای از کارها هستیم که هر کدام تعداد مشخصی از ردیف ها را از یک فایل CSV پردازش می کنند:

use App\Jobs\ImportCsv;
use Illuminate\Bus\Batch;
use Illuminate\Support\Facades\Bus;
use Throwable;
 
$batch = Bus::batch([
new ImportCsv(1, 100),
new ImportCsv(101, 200),
new ImportCsv(201, 300),
new ImportCsv(301, 400),
new ImportCsv(401, 500),
])->then(function (Batch $batch) {
// All jobs completed successfully...
})->catch(function (Batch $batch, Throwable $e) {
// First batch job failure detected...
})->finally(function (Batch $batch) {
// The batch has finished executing...
})->dispatch();
 
return $batch->id;

شناسه دسته، که ممکن است از طریق ویژگی قابل دسترسی باشد ، ممکن است برای پرس و جو از گذرگاه فرمان لاراول برای اطلاعات مربوط به دسته پس از ارسال $batch->id استفاده شود .

از آنجایی که کال بک های دسته ای سریالی می شوند و در زمان های بعدی توسط صف لاراول اجرا می شوند، نباید از $this متغیر درون callback ها استفاده کنید.

نام گذاری دسته ها

برخی از ابزارها مانند Laravel Horizon و Laravel Telescope ممکن است در صورت نامگذاری دسته ها، اطلاعات رفع اشکال کاربرپسندتری را برای دسته ها ارائه دهند. برای اختصاص یک نام دلخواه به یک دسته، می توانید name در هنگام تعریف دسته، متد را فراخوانی کنید:

$batch = Bus::batch([
// ...
])->then(function (Batch $batch) {
// All jobs completed successfully...
})->name('Import CSV')->dispatch();

اتصال دسته ای و صف

اگر می خواهید اتصال و صفی را که باید برای کارهای دسته بندی شده استفاده شود مشخص کنید، می توانید از روش onConnection و استفاده کنید onQueue . همه کارهای دسته‌ای باید در یک اتصال و صف اجرا شوند:

$batch = Bus::batch([
// ...
])->then(function (Batch $batch) {
// All jobs completed successfully...
})->onConnection('redis')->onQueue('imports')->dispatch();

زنجیر در دسته

شما می توانید با قرار دادن کارهای زنجیره ای در یک آرایه، مجموعه ای از کارهای زنجیره ای را در یک دسته تعریف کنید . به عنوان مثال، ممکن است دو زنجیره شغلی را به صورت موازی اجرا کنیم و زمانی که پردازش هر دو زنجیره کار به پایان رسید، یک callback اجرا کنیم:

use App\Jobs\ReleasePodcast;
use App\Jobs\SendPodcastReleaseNotification;
use Illuminate\Bus\Batch;
use Illuminate\Support\Facades\Bus;
 
Bus::batch([
[
new ReleasePodcast(1),
new SendPodcastReleaseNotification(1),
],
[
new ReleasePodcast(2),
new SendPodcastReleaseNotification(2),
],
])->then(function (Batch $batch) {
// ...
})->dispatch();

افزودن مشاغل به دسته ها

گاهی اوقات ممکن است اضافه کردن مشاغل اضافی به یک دسته از یک کار دسته‌ای مفید باشد. این الگو زمانی می‌تواند مفید باشد که شما نیاز دارید هزاران شغل را دسته‌بندی کنید که ممکن است ارسال آنها در طول یک درخواست وب خیلی طول بکشد. بنابراین، در عوض، ممکن است بخواهید یک دسته اولیه از کارهای "لودر" را ارسال کنید که دسته را با کارهای بیشتر هیدراته می کند:

$batch = Bus::batch([
new LoadImportBatch,
new LoadImportBatch,
new LoadImportBatch,
])->then(function (Batch $batch) {
// All jobs completed successfully...
})->name('Import Contacts')->dispatch();

در این مثال، ما از LoadImportBatch job برای هیدراته کردن دسته با کارهای اضافی استفاده خواهیم کرد. برای انجام این کار، ممکن است از روشی در نمونه دسته ای استفاده کنیم add که ممکن است از طریق متد job قابل دسترسی باشد batch :

use App\Jobs\ImportContacts;
use Illuminate\Support\Collection;
 
/**
* Execute the job.
*
* @return void
*/
public function handle()
{
if ($this->batch()->cancelled()) {
return;
}
 
$this->batch()->add(Collection::times(1000, function () {
return new ImportContacts;
}));
}

شما فقط می توانید از داخل شغلی که متعلق به همان دسته است، به یک دسته کار اضافه کنید.

بازرسی دسته ها

نمونه‌ای Illuminate\Bus\Batch که برای تماس‌های تکمیل دسته‌ای ارائه می‌شود دارای ویژگی‌ها و روش‌های مختلفی است که به شما در تعامل و بازرسی یک دسته معین از کارها کمک می‌کند:

// The UUID of the batch...
$batch->id;
 
// The name of the batch (if applicable)...
$batch->name;
 
// The number of jobs assigned to the batch...
$batch->totalJobs;
 
// The number of jobs that have not been processed by the queue...
$batch->pendingJobs;
 
// The number of jobs that have failed...
$batch->failedJobs;
 
// The number of jobs that have been processed thus far...
$batch->processedJobs();
 
// The completion percentage of the batch (0-100)...
$batch->progress();
 
// Indicates if the batch has finished executing...
$batch->finished();
 
// Cancel the execution of the batch...
$batch->cancel();
 
// Indicates if the batch has been cancelled...
$batch->cancelled();

بازگرداندن دسته ها از مسیرها

همه Illuminate\Bus\Batch نمونه‌ها قابل سریال‌سازی JSON هستند، به این معنی که می‌توانید آنها را مستقیماً از یکی از مسیرهای برنامه خود بازگردانید تا یک بار JSON حاوی اطلاعات مربوط به دسته، از جمله پیشرفت تکمیل آن را بازیابی کنید. این باعث می‌شود اطلاعات مربوط به پیشرفت دسته در UI برنامه شما نمایش داده شود.

برای بازیابی یک دسته با شناسه آن، می توانید از روش Bus نما استفاده کنید findBatch :

use Illuminate\Support\Facades\Bus;
use Illuminate\Support\Facades\Route;
 
Route::get('/batch/{batchId}', function (string $batchId) {
return Bus::findBatch($batchId);
});

لغو دسته ها

گاهی اوقات ممکن است لازم باشد اجرای یک دسته معین را لغو کنید. این را می توان با فراخوانی cancel متد در مثال زیر انجام داد Illuminate\Bus\Batch :

/**
* Execute the job.
*
* @return void
*/
public function handle()
{
if ($this->user->exceedsImportLimit()) {
return $this->batch()->cancel();
}
 
if ($this->batch()->cancelled()) {
return;
}
}

همانطور که ممکن است در مثال‌های قبلی متوجه شده باشید، کارهای دسته‌ای معمولاً باید تعیین کنند که آیا دسته مربوطه آنها قبل از ادامه اجرا لغو شده است یا خیر. با این حال، برای راحتی، می توانید به جای آن SkipIfBatchCancelled میان افزار را به کار اختصاص دهید. همانطور که از نام آن مشخص است، این میان افزار به لاراول دستور می دهد که اگر دسته مربوط به آن لغو شد، کار را پردازش نکند:

use Illuminate\Queue\Middleware\SkipIfBatchCancelled;
 
/**
* Get the middleware the job should pass through.
*
* @return array
*/
public function middleware()
{
return [new SkipIfBatchCancelled];
}

شکست دسته ای

هنگامی که یک کار دسته‌ای با شکست مواجه می‌شود، catch پاسخ تماس (در صورت اختصاص داده شده) فراخوانی می‌شود. این فراخوانی فقط برای اولین کاری که در دسته با شکست مواجه می شود فراخوانی می شود.

اجازه دادن به شکست ها

هنگامی که یک کار در یک دسته با شکست مواجه می شود، لاراول به طور خودکار دسته را به عنوان "لغو" علامت گذاری می کند. در صورت تمایل، می توانید این رفتار را غیرفعال کنید تا یک شکست شغلی به طور خودکار دسته را به عنوان لغو علامت گذاری نکند. این ممکن است با فراخوانی allowFailures متد در حین ارسال دسته انجام شود:

$batch = Bus::batch([
// ...
])->then(function (Batch $batch) {
// All jobs completed successfully...
})->allowFailures()->dispatch();

تلاش مجدد برای کارهای دسته ای ناموفق

برای راحتی کار، لاراول queue:retry-batch دستور Artisan را ارائه می دهد که به شما امکان می دهد به راحتی تمام کارهای ناموفق را برای یک دسته مشخص دوباره امتحان کنید. دستور queue:retry-batch UUID دسته‌ای را می‌پذیرد که کارهای ناموفق آن باید دوباره امتحان شوند:

php artisan queue:retry-batch 32dbc76c-4f82-4749-b610-a639fe0099b5

دسته های هرس

بدون هرس، job_batches جدول می تواند رکوردها را خیلی سریع جمع آوری کند. برای کاهش این مشکل، باید دستور Artisan را برای اجرای روزانه برنامه ریزی کنید : queue:prune-batches

$schedule->command('queue:prune-batches')->daily();

به طور پیش فرض، تمام دسته های تمام شده که بیش از 24 ساعت از عمر آنها می گذرد، هرس می شوند. می توانید hours هنگام فراخوانی فرمان از این گزینه برای تعیین مدت زمان نگهداری داده های دسته ای استفاده کنید. به عنوان مثال، دستور زیر تمام دسته هایی را که بیش از 48 ساعت قبل تمام شده اند حذف می کند:

$schedule->command('queue:prune-batches --hours=48')->daily();

گاهی اوقات، جدول شما jobs_batches ممکن است سوابق دسته‌ای را برای دسته‌هایی که هرگز با موفقیت تکمیل نشده‌اند، جمع‌آوری کند، مانند دسته‌هایی که یک کار شکست خورده و آن کار هرگز با موفقیت دوباره امتحان نشده است. می‌توانید queue:prune-batches با استفاده از این گزینه دستور هرس کردن این رکوردهای دسته‌ای ناتمام را بدهید unfinished :

$schedule->command('queue:prune-batches --hours=48 --unfinished=72')->daily();

به همین ترتیب، جدول شما jobs_batches همچنین ممکن است سوابق دسته ای را برای دسته های لغو شده جمع آوری کند. می‌توانید queue:prune-batches با استفاده از این گزینه دستور هرس کردن این رکوردهای دسته‌ای لغو شده را بدهید cancelled :

$schedule->command('queue:prune-batches --hours=48 --cancelled=72')->daily();

بسته شدن صف

به جای اعزام یک کلاس شغلی به صف، ممکن است یک تعطیلی نیز ارسال کنید. این برای کارهای سریع و ساده که باید خارج از چرخه درخواست فعلی اجرا شوند عالی است. هنگام ارسال بسته‌ها به صف، محتوای کد بسته‌بندی به‌صورت رمزنگاری امضا می‌شود تا در حین انتقال قابل تغییر نباشد:

$podcast = App\Podcast::find(1);
 
dispatch(function () use ($podcast) {
$podcast->publish();
});

با استفاده از این catch روش، می‌توانید بسته‌ای را ارائه دهید که اگر بسته شدن در صف پس از اتمام تمام تلاش‌های مجدد پیکربندی شده صف شما با موفقیت کامل نشد، باید اجرا شود :

use Throwable;
 
dispatch(function () use ($podcast) {
$podcast->publish();
})->catch(function (Throwable $e) {
// This job has failed...
});

از آنجایی که catch کال بک ها سریالی می شوند و در زمان های بعدی توسط صف لاراول اجرا می شوند، نباید از $this متغیر درون catch callback ها استفاده کنید.

اجرای The Queue Worker

دستور queue:work _

لاراول شامل یک دستور Artisan است که یک queue worker را راه اندازی می کند و کارهای جدید را همانطور که در صف قرار می دهند پردازش می کند. شما می توانید کارگر را با استفاده از queue:work دستور Artisan اجرا کنید. توجه داشته باشید که پس از queue:work شروع فرمان، تا زمانی که به صورت دستی متوقف شود یا ترمینال خود را ببندید، به اجرا ادامه خواهد داد:

php artisan queue:work

برای اینکه queue:work فرآیند به‌طور دائم در پس‌زمینه اجرا شود، باید از یک مانیتور فرآیند مانند Supervisor استفاده کنید تا مطمئن شوید که کارفرمای صف متوقف نمی‌شود.

اگر می‌خواهید شناسه‌های شغلی پردازش‌شده در خروجی فرمان گنجانده شوند، می‌توانید -v هنگام فراخوانی دستور، پرچم را اضافه کنید: queue:work

php artisan queue:work -v

Remember, queue workers are long-lived processes and store the booted application state in memory. As a result, they will not notice changes in your code base after they have been started. So, during your deployment process, be sure to restart your queue workers. In addition, remember that any static state created or modified by your application will not be automatically reset between jobs.

Alternatively, you may run the queue:listen command. When using the queue:listen command, you don't have to manually restart the worker when you want to reload your updated code or reset the application state; however, this command is significantly less efficient than the queue:work command:

php artisan queue:listen

Running Multiple Queue Workers

To assign multiple workers to a queue and process jobs concurrently, you should simply start multiple queue:work processes. This can either be done locally via multiple tabs in your terminal or in production using your process manager's configuration settings. When using Supervisor, you may use the numprocs configuration value.

Specifying The Connection & Queue

You may also specify which queue connection the worker should utilize. The connection name passed to the work command should correspond to one of the connections defined in your config/queue.php configuration file:

php artisan queue:work redis

By default, the queue:work command only processes jobs for the default queue on a given connection. However, you may customize your queue worker even further by only processing particular queues for a given connection. For example, if all of your emails are processed in an emails queue on your redis queue connection, you may issue the following command to start a worker that only processes that queue:

php artisan queue:work redis --queue=emails

Processing A Specified Number Of Jobs

The --once option may be used to instruct the worker to only process a single job from the queue:

php artisan queue:work --once

The --max-jobs option may be used to instruct the worker to process the given number of jobs and then exit. This option may be useful when combined with Supervisor so that your workers are automatically restarted after processing a given number of jobs, releasing any memory they may have accumulated:

php artisan queue:work --max-jobs=1000

Processing All Queued Jobs & Then Exiting

The --stop-when-empty option may be used to instruct the worker to process all jobs and then exit gracefully. This option can be useful when processing Laravel queues within a Docker container if you wish to shutdown the container after the queue is empty:

php artisan queue:work --stop-when-empty

Processing Jobs For A Given Number Of Seconds

The --max-time option may be used to instruct the worker to process jobs for the given number of seconds and then exit. This option may be useful when combined with Supervisor so that your workers are automatically restarted after processing jobs for a given amount of time, releasing any memory they may have accumulated:

# Process jobs for one hour and then exit...
php artisan queue:work --max-time=3600

Worker Sleep Duration

When jobs are available on the queue, the worker will keep processing jobs with no delay in between jobs. However, the sleep option determines how many seconds the worker will "sleep" if there are no jobs available. Of course, while sleeping, the worker will not process any new jobs:

php artisan queue:work --sleep=3

Resource Considerations

Daemon queue workers do not "reboot" the framework before processing each job. Therefore, you should release any heavy resources after each job completes. For example, if you are doing image manipulation with the GD library, you should free the memory with imagedestroy when you are done processing the image.

Queue Priorities

Sometimes you may wish to prioritize how your queues are processed. For example, in your config/queue.php configuration file, you may set the default queue for your redis connection to low. However, occasionally you may wish to push a job to a high priority queue like so:

dispatch((new Job)->onQueue('high'));

To start a worker that verifies that all of the high queue jobs are processed before continuing to any jobs on the low queue, pass a comma-delimited list of queue names to the work command:

php artisan queue:work --queue=high,low

Queue Workers & Deployment

Since queue workers are long-lived processes, they will not notice changes to your code without being restarted. So, the simplest way to deploy an application using queue workers is to restart the workers during your deployment process. You may gracefully restart all of the workers by issuing the queue:restart command:

php artisan queue:restart

This command will instruct all queue workers to gracefully exit after they finish processing their current job so that no existing jobs are lost. Since the queue workers will exit when the queue:restart command is executed, you should be running a process manager such as Supervisor to automatically restart the queue workers.

The queue uses the cache to store restart signals, so you should verify that a cache driver is properly configured for your application before using this feature.

Job Expirations & Timeouts

Job Expiration

در فایل پیکربندی شما config/queue.php ، هر اتصال صف یک retry_after گزینه را تعریف می کند. این گزینه مشخص می کند که اتصال صف باید چند ثانیه منتظر بماند تا کاری که در حال پردازش است دوباره امتحان شود. به عنوان مثال، اگر مقدار روی retry_after تنظیم شود 90 ، اگر کار به مدت 90 ثانیه بدون آزاد شدن یا حذف شدن پردازش شده باشد، دوباره در صف قرار می گیرد. به طور معمول، شما باید retry_after مقدار را روی حداکثر تعداد ثانیه هایی که کار شما به طور منطقی باید طول بکشد تا پردازش کامل شود، تنظیم کنید.

تنها اتصال صفی که حاوی مقدار نیست retry_after Amazon SQS است. SQS کار را بر اساس پیش‌فرض زمان نمایان بودن که در کنسول AWS مدیریت می‌شود، دوباره امتحان می‌کند.

تایم اوت های کارگری

دستور queue:work Artisan یک --timeout گزینه را آشکار می کند. به طور پیش فرض، --timeout مقدار 60 ثانیه است. اگر یک کار بیش از تعداد ثانیه های مشخص شده توسط مقدار زمان وقفه در حال پردازش باشد، کارگری که کار را پردازش می کند با خطا خارج می شود. به طور معمول، worker به طور خودکار توسط مدیر فرآیند پیکربندی شده روی سرور شما راه اندازی مجدد می شود :

php artisan queue:work --timeout=60

گزینه پیکربندی retry_after و --timeout گزینه CLI متفاوت هستند، اما با هم کار می کنند تا اطمینان حاصل شود که مشاغل از بین نمی روند و کارها فقط یک بار با موفقیت پردازش می شوند.

مقدار --timeout باید همیشه حداقل چند ثانیه کوتاهتر از retry_after مقدار پیکربندی شما باشد. این تضمین می کند که کارگری که یک کار منجمد شده را پردازش می کند همیشه قبل از امتحان مجدد کار خاتمه می یابد. اگر --timeout گزینه شما بیشتر از retry_after مقدار پیکربندی شما باشد، ممکن است کارهای شما دو بار پردازش شود.

پیکربندی سرپرست

در تولید، شما به راهی نیاز دارید که queue:work فرآیندهای خود را در حال اجرا نگه دارید. ممکن است یک queue:work فرآیند به دلایل مختلفی از جمله مهلت زمانی بیش از حد کارگر یا اجرای فرمان متوقف شود queue:restart .

به همین دلیل، باید یک مانیتور فرآیند را پیکربندی کنید که بتواند زمان queue:work خروج فرآیندهای شما را تشخیص دهد و به طور خودکار آنها را راه اندازی مجدد کند. علاوه بر این، مانیتورهای فرآیند می توانند به شما این امکان را بدهند که تعیین کنید چند queue:work فرآیند را می خواهید همزمان اجرا کنید. Supervisor یک مانیتور فرآیند است که معمولاً در محیط‌های لینوکس استفاده می‌شود و نحوه پیکربندی آن را در مستندات زیر توضیح خواهیم داد.

نصب سوپروایزر

Supervisor یک مانیتور فرآیند برای سیستم عامل لینوکس است و queue:work در صورت عدم موفقیت فرآیندهای شما را به طور خودکار راه اندازی مجدد می کند. برای نصب Supervisor در اوبونتو، می توانید از دستور زیر استفاده کنید:

sudo apt-get install supervisor

اگر پیکربندی و مدیریت Supervisor به نظر سخت می رسد، از Laravel Forge استفاده کنید ، که به طور خودکار Supervisor را برای پروژه های Laravel تولیدی شما نصب و پیکربندی می کند.

پیکربندی سرپرست

فایل های پیکربندی ناظر معمولاً در /etc/supervisor/conf.d دایرکتوری ذخیره می شوند. در این فهرست، می‌توانید هر تعداد فایل پیکربندی ایجاد کنید که به سرپرست آموزش می‌دهد که چگونه فرآیندهای شما باید نظارت شوند. به عنوان مثال، بیایید یک laravel-worker.conf فایل ایجاد کنیم که queue:work فرآیندها را شروع و نظارت کند:

[program:laravel-worker]
process_name=%(program_name)s_%(process_num)02d
command=php /home/forge/app.com/artisan queue:work sqs --sleep=3 --tries=3 --max-time=3600
autostart=true
autorestart=true
stopasgroup=true
killasgroup=true
user=forge
numprocs=8
redirect_stderr=true
stdout_logfile=/home/forge/app.com/worker.log
stopwaitsecs=3600

در این مثال، numprocs دستورالعمل به Supervisor دستور می دهد تا هشت queue:work فرآیند را اجرا کند و همه آنها را نظارت کند و در صورت شکست، به طور خودکار آنها را مجدداً راه اندازی کند. شما باید command دستورالعمل پیکربندی را تغییر دهید تا اتصال صف مورد نظر و گزینه های کارگر را منعکس کند.

باید اطمینان حاصل کنید که ارزش آن stopwaitsecs از تعداد ثانیه های مصرف شده توسط طولانی ترین کار شما بیشتر است. در غیر این صورت، Supervisor ممکن است کار را قبل از اتمام پردازش آن از بین ببرد.

راه اندازی سرپرست

پس از ایجاد فایل پیکربندی، می‌توانید پیکربندی Supervisor را به‌روزرسانی کنید و فرآیندها را با استفاده از دستورات زیر شروع کنید:

sudo supervisorctl reread
 
sudo supervisorctl update
 
sudo supervisorctl start laravel-worker:*

برای اطلاعات بیشتر در مورد Supervisor، به مستندات Supervisor مراجعه کنید .

مقابله با مشاغل ناموفق

گاهی اوقات مشاغل در صف شما با شکست مواجه می شوند. نگران نباشید، همه چیز همیشه طبق برنامه پیش نمی رود! لاراول شامل یک راه راحت برای تعیین حداکثر تعداد دفعاتی است که یک کار باید انجام شود . پس از اینکه یک کار ناهمزمان از این تعداد تلاش بیشتر شد، در جدول پایگاه داده درج می شود failed_jobs . کارهای ارسالی همزمان که با شکست مواجه می شوند در این جدول ذخیره نمی شوند و استثناهای آنها بلافاصله توسط برنامه مدیریت می شود.

مهاجرت برای ایجاد failed_jobs جدول معمولاً در برنامه های جدید لاراول وجود دارد. با این حال، اگر برنامه شما حاوی یک انتقال برای این جدول نیست، می توانید از queue:failed-table دستور برای ایجاد مهاجرت استفاده کنید:

php artisan queue:failed-table
 
php artisan migrate

هنگام اجرای یک فرآیند queue worker ، می‌توانید حداکثر تعداد دفعاتی که یک کار باید با استفاده از --tries سوئیچ روی queue:work دستور انجام شود را مشخص کنید. اگر مقداری را برای گزینه مشخص نکنید --tries ، jobها فقط یک بار یا به تعداد مشخص شده توسط $tries ویژگی کلاس شغلی انجام خواهند شد:

php artisan queue:work redis --tries=3

با استفاده از این --backoff گزینه، می‌توانید مشخص کنید که لاراول چند ثانیه قبل از امتحان مجدد کاری که با استثنا مواجه شده است، صبر کند. به‌طور پیش‌فرض، یک کار فوراً به صف باز می‌گردد تا دوباره تلاش شود:

php artisan queue:work redis --tries=3 --backoff=3

اگر می خواهید تنظیم کنید که لاراول چند ثانیه باید قبل از امتحان مجدد کاری که بر اساس هر شغل با استثنا مواجه شده است، منتظر بماند، می توانید این کار را با تعریف یک backoff ویژگی در کلاس شغلی خود انجام دهید:

/**
* The number of seconds to wait before retrying the job.
*
* @var int
*/
public $backoff = 3;

اگر به منطق پیچیده تری برای تعیین زمان عقب نشینی شغل نیاز دارید، می توانید روشی را backoff در کلاس شغلی خود تعریف کنید:

/**
* Calculate the number of seconds to wait before retrying the job.
*
* @return int
*/
public function backoff()
{
return 3;
}

شما می توانید به راحتی با برگرداندن آرایه ای از مقادیر backoff از backoff متد، پشتیبان های "نمایی" را پیکربندی کنید. در این مثال، تأخیر در تلاش مجدد 1 ثانیه برای اولین بار، 5 ثانیه برای تلاش مجدد دوم و 10 ثانیه برای تلاش مجدد سوم خواهد بود:

/**
* Calculate the number of seconds to wait before retrying the job.
*
* @return array
*/
public function backoff()
{
return [1, 5, 10];
}

تمیز کردن پس از انجام کارهای ناموفق

هنگامی که یک کار خاص با شکست مواجه می شود، ممکن است بخواهید یک هشدار برای کاربران خود ارسال کنید یا هر اقدامی را که تا حدی توسط کار انجام شده است، برگردانید. برای انجام این کار، ممکن است failed روشی را در کلاس شغلی خود تعریف کنید. نمونه ای Throwable که باعث شکست کار شده است به failed متد ارسال می شود:

<?php
 
namespace App\Jobs;
 
use App\Models\Podcast;
use App\Services\AudioProcessor;
use Illuminate\Bus\Queueable;
use Illuminate\Contracts\Queue\ShouldQueue;
use Illuminate\Queue\InteractsWithQueue;
use Illuminate\Queue\SerializesModels;
use Throwable;
 
class ProcessPodcast implements ShouldQueue
{
use InteractsWithQueue, Queueable, SerializesModels;
 
/**
* The podcast instance.
*
* @var \App\Podcast
*/
public $podcast;
 
/**
* Create a new job instance.
*
* @param \App\Models\Podcast $podcast
* @return void
*/
public function __construct(Podcast $podcast)
{
$this->podcast = $podcast;
}
 
/**
* Execute the job.
*
* @param \App\Services\AudioProcessor $processor
* @return void
*/
public function handle(AudioProcessor $processor)
{
// Process uploaded podcast...
}
 
/**
* Handle a job failure.
*
* @param \Throwable $exception
* @return void
*/
public function failed(Throwable $exception)
{
// Send user notification of failure, etc...
}
}

یک نمونه جدید از کار قبل از فراخوانی failed روش نمونه سازی می شود. بنابراین، هر گونه تغییر ویژگی کلاس که ممکن است در handle متد رخ داده باشد از بین خواهد رفت.

تلاش مجدد مشاغل ناموفق

برای مشاهده تمام کارهای ناموفق که در failed_jobs جدول پایگاه داده شما درج شده اند، می توانید از queue:failed دستور Artisan استفاده کنید:

php artisan queue:failed

این queue:failed فرمان شناسه شغل، اتصال، صف، زمان شکست و سایر اطلاعات مربوط به کار را فهرست می کند. شناسه شغل ممکن است برای امتحان مجدد کار ناموفق استفاده شود. به عنوان مثال، برای امتحان مجدد یک کار ناموفق که دارای شناسه است ce7bb17c-cdd8-41f0-a8ec-7b4fef4e5ece ، دستور زیر را صادر کنید:

php artisan queue:retry ce7bb17c-cdd8-41f0-a8ec-7b4fef4e5ece

در صورت لزوم، می توانید چندین شناسه را به دستور ارسال کنید:

php artisan queue:retry ce7bb17c-cdd8-41f0-a8ec-7b4fef4e5ece 91401d2c-0784-4f43-824c-34f94a33c24d

همچنین می‌توانید همه کارهای ناموفق را برای یک صف خاص دوباره امتحان کنید:

php artisan queue:retry --queue=name

برای امتحان مجدد همه کارهای ناموفق خود، queue:retry دستور را اجرا کرده و all به عنوان شناسه ارسال کنید:

php artisan queue:retry all

اگر می خواهید یک کار ناموفق را حذف کنید، می توانید از queue:forget دستور زیر استفاده کنید:

php artisan queue:forget 91401d2c-0784-4f43-824c-34f94a33c24d

هنگام استفاده از Horizon ، باید از horizon:forget دستور حذف یک کار ناموفق به جای queue:forget دستور استفاده کنید.

برای حذف تمام کارهای ناموفق خود از failed_jobs جدول، می توانید از queue:flush دستور زیر استفاده کنید:

php artisan queue:flush

نادیده گرفتن مدل های گمشده

هنگام تزریق یک مدل Eloquent به یک کار، مدل قبل از قرار گرفتن در صف به طور خودکار سریال سازی می شود و در هنگام پردازش کار دوباره از پایگاه داده بازیابی می شود. با این حال، اگر در زمانی که کار در انتظار پردازش توسط کارگر بود، مدل حذف شده باشد، ممکن است کار شما با یک علامت شکست بخورد ModelNotFoundException .

برای راحتی کار، می‌توانید با تنظیم deleteWhenMissingModels ویژگی شغل خود به صورت خودکار، کارهای دارای مدل‌های گمشده را حذف کنید true . هنگامی که این ویژگی روی تنظیم شود true ، لاراول بی سر و صدا کار را بدون ذکر استثنا کنار می گذارد:

/**
* Delete the job if its models no longer exist.
*
* @var bool
*/
public $deleteWhenMissingModels = true;

هرس مشاغل ناموفق

می توانید رکوردهای failed_jobs جدول برنامه خود را با فراخوانی queue:prune-failed دستور Artisan هرس کنید:

php artisan queue:prune-failed

به طور پیش فرض، تمام سوابق شغلی ناموفق که بیش از 24 ساعت از آن می گذرد، هرس می شوند. اگر این گزینه را برای فرمان ارائه دهید --hours ، فقط سوابق شغلی ناموفق که در N ساعت گذشته درج شده اند، حفظ خواهند شد. به عنوان مثال، دستور زیر تمام سوابق شغلی ناموفق را که بیش از 48 ساعت قبل درج شده اند حذف می کند:

php artisan queue:prune-failed --hours=48

ذخیره مشاغل ناموفق در DynamoDB

لاراول همچنین از ذخیره سوابق شغلی ناموفق شما در DynamoDB به جای جدول پایگاه داده رابطه ای پشتیبانی می کند. با این حال، شما باید یک جدول DynamoDB ایجاد کنید تا تمام رکوردهای کار ناموفق را ذخیره کنید. به طور معمول، این جدول باید نامگذاری شود failed_jobs ، اما شما باید جدول را بر اساس مقدار مقدار queue.failed.table پیکربندی در فایل پیکربندی برنامه خود نامگذاری کنید queue .

جدول failed_jobs باید دارای یک کلید پارتیشن اصلی رشته ای به نام application و یک کلید مرتب سازی اولیه رشته به نام باشد uuid . بخشی application از کلید شامل نام برنامه شما خواهد بود که توسط name مقدار پیکربندی در فایل پیکربندی برنامه شما تعریف شده است app . از آنجایی که نام برنامه بخشی از کلید جدول DynamoDB است، می توانید از همان جدول برای ذخیره کارهای ناموفق برای چندین برنامه لاراول استفاده کنید.

علاوه بر این، اطمینان حاصل کنید که AWS SDK را نصب کرده اید تا برنامه لاراول شما بتواند با آمازون DynamoDB ارتباط برقرار کند:

composer require aws/aws-sdk-php

در مرحله بعد، queue.failed.driver مقدار گزینه پیکربندی را روی dynamodb . علاوه بر این، باید گزینه های پیکربندی key ، secret و region پیکربندی را در آرایه پیکربندی کار ناموفق تعریف کنید. این گزینه ها برای احراز هویت با AWS استفاده خواهند شد. هنگام استفاده از dynamodb درایور، queue.failed.database گزینه پیکربندی غیر ضروری است:

'failed' => [
'driver' => env('QUEUE_FAILED_DRIVER', 'dynamodb'),
'key' => env('AWS_ACCESS_KEY_ID'),
'secret' => env('AWS_SECRET_ACCESS_KEY'),
'region' => env('AWS_DEFAULT_REGION', 'us-east-1'),
'table' => 'failed_jobs',
],

غیرفعال کردن ذخیره‌سازی کار ناموفق

شما می توانید با تنظیم queue.failed.driver مقدار گزینه پیکربندی روی null . به طور معمول، این ممکن است از طریق QUEUE_FAILED_DRIVER متغیر محیطی انجام شود:

QUEUE_FAILED_DRIVER=null

رویدادهای شغلی ناموفق

اگر می خواهید شنونده رویدادی را ثبت کنید که در صورت شکست کاری فراخوانی می شود، می توانید از روش Queue نما استفاده کنید failing . به عنوان مثال، ممکن است از boot روشی AppServiceProvider که در لاراول گنجانده شده است، یک بسته به این رویداد اضافه کنیم :

<?php
 
namespace App\Providers;
 
use Illuminate\Support\Facades\Queue;
use Illuminate\Support\ServiceProvider;
use Illuminate\Queue\Events\JobFailed;
 
class AppServiceProvider extends ServiceProvider
{
/**
* Register any application services.
*
* @return void
*/
public function register()
{
//
}
 
/**
* Bootstrap any application services.
*
* @return void
*/
public function boot()
{
Queue::failing(function (JobFailed $event) {
// $event->connectionName
// $event->job
// $event->exception
});
}
}

پاک کردن مشاغل از صف

هنگام استفاده از Horizon ، باید horizon:clear به جای دستور، از دستور پاک کردن مشاغل از صف استفاده کنید queue:clear .

اگر می‌خواهید همه کارها را از صف پیش‌فرض اتصال پیش‌فرض حذف کنید، می‌توانید با استفاده از queue:clear دستور Artisan این کار را انجام دهید:

php artisan queue:clear

همچنین می‌توانید connection آرگومان و queue گزینه‌ای برای حذف کارها از یک اتصال و صف خاص ارائه دهید:

php artisan queue:clear redis --queue=emails

پاک کردن کارها از صف ها فقط برای درایورهای صف SQS، Redis و پایگاه داده در دسترس است. علاوه بر این، فرآیند حذف پیام SQS تا 60 ثانیه طول می کشد، بنابراین کارهای ارسال شده به صف SQS تا 60 ثانیه پس از پاک کردن صف نیز ممکن است حذف شوند.

نظارت بر صف های شما

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

برای شروع، باید queue:monitor دستور را برای اجرای هر دقیقه برنامه ریزی کنید . این دستور نام صف هایی را که می خواهید نظارت کنید و همچنین آستانه تعداد کار مورد نظر شما را می پذیرد:

php artisan queue:monitor redis:default,redis:deployments --max=100

زمان‌بندی این دستور به تنهایی برای راه‌اندازی یک اعلان که وضعیت بیش از حد صف را به شما هشدار می‌دهد کافی نیست. هنگامی که دستور با صفی مواجه می شود که تعداد کار از آستانه شما بیشتر است، یک Illuminate\Queue\Events\QueueBusy رویداد ارسال می شود. می‌توانید به این رویداد در برنامه‌تان گوش دهید EventServiceProvider تا یک اعلان برای شما یا تیم توسعه‌تان ارسال شود:

use App\Notifications\QueueHasLongWaitTime;
use Illuminate\Queue\Events\QueueBusy;
use Illuminate\Support\Facades\Event;
use Illuminate\Support\Facades\Notification;
 
/**
* Register any other events for your application.
*
* @return void
*/
public function boot()
{
Event::listen(function (QueueBusy $event) {
Notification::route('mail', 'dev@example.com')
->notify(new QueueHasLongWaitTime(
$event->connection,
$event->queue,
$event->size
));
});
}

رویدادهای شغلی

با استفاده از before و after متدهای روی Queue نما ، می‌توانید فراخوان‌هایی را مشخص کنید که قبل یا بعد از پردازش یک کار در صف اجرا شوند. این فراخوان ها فرصتی عالی برای انجام ثبت گزارش یا آمار افزایشی برای داشبورد هستند. به طور معمول، شما باید این روش ها را از boot روش ارائه دهنده خدمات فراخوانی کنید . به عنوان مثال، ممکن است از چیزی AppServiceProvider که در لاراول موجود است استفاده کنیم :

<?php
 
namespace App\Providers;
 
use Illuminate\Support\Facades\Queue;
use Illuminate\Support\ServiceProvider;
use Illuminate\Queue\Events\JobProcessed;
use Illuminate\Queue\Events\JobProcessing;
 
class AppServiceProvider extends ServiceProvider
{
/**
* Register any application services.
*
* @return void
*/
public function register()
{
//
}
 
/**
* Bootstrap any application services.
*
* @return void
*/
public function boot()
{
Queue::before(function (JobProcessing $event) {
// $event->connectionName
// $event->job
// $event->job->payload()
});
 
Queue::after(function (JobProcessed $event) {
// $event->connectionName
// $event->job
// $event->job->payload()
});
}
}

با استفاده از looping روش روی Queue نما ، می‌توانید فراخوان‌هایی را مشخص کنید که قبل از تلاش کارگر برای واکشی کاری از یک صف اجرا شوند. به عنوان مثال، ممکن است برای بازگرداندن تراکنش‌هایی که توسط یک کار شکست خورده قبلی باز مانده بودند، بسته شدن را ثبت کنید:

use Illuminate\Support\Facades\DB;
use Illuminate\Support\Facades\Queue;
 
Queue::looping(function () {
while (DB::transactionLevel() > 0) {
DB::rollBack();
}
});