نسخه:

صف ها

معرفی

در حین ساختن برنامه وب خود، ممکن است وظایفی داشته باشید، مانند تجزیه و ذخیره یک فایل 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 درایور صف، به یک جدول پایگاه داده برای نگهداری کارها نیاز دارید. به طور معمول، این در 0001_01_01_000002_create_jobs_table.php انتقال پایگاه داده پیش فرض لاراول گنجانده شده است . با این حال، اگر برنامه شما حاوی این مهاجرت نیست، می توانید از make:queue-table دستور Artisan برای ایجاد آن استفاده کنید:

php artisan make:queue-table
 
php artisan migrate

ردیس

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

گزینه های serializer و compression Redis توسط درایور صف پشتیبانی نمی شوند redis .

خوشه ردیس

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

'redis' => [
'driver' => 'redis',
'connection' => env('REDIS_QUEUE_CONNECTION', 'default'),
'queue' => env('REDIS_QUEUE', '{default}'),
'retry_after' => env('REDIS_QUEUE_RETRY_AFTER', 90),
'block_for' => null,
'after_commit' => false,
],

مسدود کردن

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

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

'redis' => [
'driver' => 'redis',
'connection' => env('REDIS_QUEUE_CONNECTION', 'default'),
'queue' => env('REDIS_QUEUE', 'default'),
'retry_after' => env('REDIS_QUEUE_RETRY_AFTER', 90),
'block_for' => 5,
'after_commit' => false,
],

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

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

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

  • آمازون SQS: aws/aws-sdk-php ~3.0
  • Beanstalkd: pda/pheanstalk ~5.0
  • Redis: predis/predis ~2.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;
 
/**
* Create a new job instance.
*/
public function __construct(
public Podcast $podcast,
) {}
 
/**
* Execute the job.
*/
public function handle(AudioProcessor $processor): void
{
// 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;
use Illuminate\Contracts\Foundation\Application;
 
$this->app->bindMethod([ProcessPodcast::class, 'handle'], function (ProcessPodcast $job, Application $app) {
return $job->handle($app->make(AudioProcessor::class));
});

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

روابط در صف

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

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

/**
* Create a new job instance.
*/
public function __construct(Podcast $podcast)
{
$this->podcast = $podcast->withoutRelations();
}

اگر از ارتقای ویژگی سازنده PHP استفاده می کنید و می خواهید نشان دهید که یک مدل Eloquent نباید روابط خود را سریالی کند، می توانید از WithoutRelations ویژگی استفاده کنید:

use Illuminate\Queue\Attributes\WithoutRelations;
 
/**
* Create a new job instance.
*/
public function __construct(
#[WithoutRelations]
public Podcast $podcast
) {
}

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

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

مشاغل منحصر به فرد نیاز به یک درایور حافظه پنهان دارند که از قفل ها پشتیبانی می کند . در حال حاضر، درایورهای 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;
 
/**
* Get the unique ID for the job.
*/
public function uniqueId(): string
{
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\Contracts\Cache\Repository;
use Illuminate\Support\Facades\Cache;
 
class UpdateSearchIndex implements ShouldQueue, ShouldBeUnique
{
...
 
/**
* Get the cache driver for the unique job lock.
*/
public function uniqueVia(): Repository
{
return Cache::driver('redis');
}
}

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

مشاغل رمزگذاری شده

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

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

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

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

use Illuminate\Support\Facades\Redis;
 
/**
* Execute the job.
*/
public function handle(): void
{
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 Closure;
use Illuminate\Support\Facades\Redis;
 
class RateLimited
{
/**
* Process the queued job.
*
* @param \Closure(object): void $next
*/
public function handle(object $job, Closure $next): void
{
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<int, object>
*/
public function middleware(): array
{
return [new RateLimited];
}

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

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

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

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

use Illuminate\Cache\RateLimiting\Limit;
use Illuminate\Support\Facades\RateLimiter;
 
/**
* Bootstrap any application services.
*/
public function boot(): void
{
RateLimiter::for('backups', function (object $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<int, object>
*/
public function middleware(): array
{
return [new RateLimited('backups')];
}

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

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

/**
* Get the middleware the job should pass through.
*
* @return array<int, object>
*/
public function middleware(): array
{
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<int, object>
*/
public function middleware(): array
{
return [new WithoutOverlapping($this->user->id)];
}

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

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

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

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

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

/**
* Get the middleware the job should pass through.
*
* @return array<int, object>
*/
public function middleware(): array
{
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(): array
{
return [
(new WithoutOverlapping("status:{$this->provider}"))->shared(),
];
}
}
 
class ProviderIsUp
{
// ...
 
 
public function middleware(): array
{
return [
(new WithoutOverlapping("status:{$this->provider}"))->shared(),
];
}
}

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

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

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

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

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

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

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

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

use Illuminate\Queue\Middleware\ThrottlesExceptions;
 
/**
* Get the middleware the job should pass through.
*
* @return array<int, object>
*/
public function middleware(): array
{
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\RedirectResponse;
use Illuminate\Http\Request;
 
class PodcastController extends Controller
{
/**
* Store a new podcast.
*/
public function store(Request $request): RedirectResponse
{
$podcast = Podcast::create(/* ... */);
 
// ...
 
ProcessPodcast::dispatch($podcast);
 
return redirect('/podcasts');
}
}

اگر می خواهید کاری را به صورت مشروط ارسال کنید، می توانید از روش های 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\RedirectResponse;
use Illuminate\Http\Request;
 
class PodcastController extends Controller
{
/**
* Store a new podcast.
*/
public function store(Request $request): RedirectResponse
{
$podcast = Podcast::create(/* ... */);
 
// ...
 
ProcessPodcast::dispatch($podcast)
->delay(now()->addMinutes(10));
 
return redirect('/podcasts');
}
}

سرویس صف آمازون 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\RedirectResponse;
use Illuminate\Http\Request;
 
class PodcastController extends Controller
{
/**
* Store a new podcast.
*/
public function store(Request $request): RedirectResponse
{
$podcast = Podcast::create(/* ... */);
 
// Create podcast...
 
ProcessPodcast::dispatchSync($podcast);
 
return redirect('/podcasts');
}
}

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

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

خوشبختانه، لاراول چندین روش برای حل این مشکل ارائه می دهد. ابتدا می توانید after_commit گزینه اتصال را در آرایه پیکربندی اتصال صف خود تنظیم کنید:

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

هنگامی که after_commit گزینه وجود دارد true ، می توانید کارها را در تراکنش های پایگاه داده ارسال کنید. با این حال، لاراول منتظر می ماند تا تراکنش های پایگاه داده والد باز انجام شود و سپس کار را ارسال کند. البته، اگر در حال حاضر هیچ تراکنش پایگاه داده باز نباشد، کار بلافاصله ارسال می شود.

اگر یک تراکنش به دلیل استثنایی که در طول تراکنش رخ می‌دهد، برگشت داده شود، کارهایی که در طی آن تراکنش ارسال شده‌اند کنار گذاشته می‌شوند.

تنظیم after_commit گزینه پیکربندی روی true همچنین باعث می‌شود هر شنونده رویداد، پیام‌های پستی، اعلان‌ها و رویدادهای پخش در صف پس از انجام تمام تراکنش‌های پایگاه داده باز ارسال شود.

مشخص کردن Commit Dispatch Behavior Inline

after_commit اگر گزینه پیکربندی اتصال صف را بر روی تنظیم نکنید true ، همچنان ممکن است نشان دهید که یک کار خاص باید پس از انجام تمام تراکنش های پایگاه داده باز ارسال شود. برای انجام این کار، می توانید afterCommit روش را به عملیات اعزام خود زنجیره بزنید:

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

به همین ترتیب، اگر after_commit گزینه پیکربندی روی تنظیم شده باشد true ، ممکن است نشان دهید که یک کار خاص باید فوراً بدون انتظار برای انجام هرگونه تراکنش پایگاه داده باز ارسال شود:

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

زنجیره شغلی

Job chaining به شما امکان می دهد لیستی از کارهای در صف را مشخص کنید که باید پس از اجرای موفقیت آمیز کار اصلی به ترتیب اجرا شوند. اگر یک کار در دنباله شکست بخورد، بقیه کارها اجرا نمی شوند. برای اجرای یک زنجیره شغلی در صف، می توانید از chain روش ارائه شده توسط Bus نما استفاده کنید. گذرگاه فرمان لاراول یک جزء سطح پایین‌تر است که ارسال کار در صف در بالای آن ساخته شده است:

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();

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

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

حذف مشاغل با استفاده از $this->delete() روش درون job مانع از پردازش مشاغل زنجیره ای نمی شود. زنجیره فقط در صورتی اجرا نمی شود که یک کار در زنجیره با شکست مواجه شود.

اتصال زنجیره ای و صف

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

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

شکست های زنجیره ای

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

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();

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

سفارشی کردن صف یک اتصال

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

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

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

از طرف دیگر، می‌توانید صف شغل را با فراخوانی 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.
*/
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\RedirectResponse;
use Illuminate\Http\Request;
 
class PodcastController extends Controller
{
/**
* Store a new podcast.
*/
public function store(Request $request): RedirectResponse
{
$podcast = Podcast::create(/* ... */);
 
// Create podcast...
 
ProcessPodcast::dispatch($podcast)->onConnection('sqs');
 
return redirect('/podcasts');
}
}

می‌توانید برای تعیین اتصال و صف برای یک کار، 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.
*/
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;
}

اگر به کنترل پویا روی حداکثر تلاش های یک کار خاص نیاز دارید، می توانید tries روشی را در آن کار تعریف کنید:

/**
* Determine number of times the job may be attempted.
*/
public function tries(): int
{
return 5;
}

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

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

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

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

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

گاهی اوقات ممکن است بخواهید مشخص کنید که یک کار ممکن است بارها انجام شود، اما اگر تلاش‌های مجدد توسط تعداد معینی از استثناهای کنترل‌نشده (برخلاف انتشار release مستقیم توسط روش) انجام شود، باید شکست بخورد. برای انجام این کار، می توانید یک maxExceptions ویژگی را در کلاس شغلی خود تعریف کنید:

<?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.
*/
public function handle(): void
{
Redis::throttle('key')->allow(10)->every(60)->then(function () {
// Lock obtained, process the podcast...
}, function () {
// Unable to obtain lock...
return $this->release(10);
});
}
}

در این مثال، اگر برنامه نتواند قفل Redis را به دست آورد، کار به مدت ده ثانیه آزاد می شود و تا 25 بار دوباره امتحان می شود. با این حال، اگر سه استثناء کنترل نشده توسط کار ایجاد شود، کار شکست خواهد خورد.

تایم اوت

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

حداکثر تعداد ثانیه‌ای که کارها می‌توانند اجرا شوند را می‌توان با استفاده از --timeout سوئیچ در خط فرمان Artisan مشخص کرد:

php artisan queue:work --timeout=30

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

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

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

گاهی اوقات، فرآیندهای مسدودکننده IO مانند سوکت ها یا اتصالات HTTP خروجی ممکن است به زمان تعیین شده شما احترام نگذارند. بنابراین، هنگام استفاده از این ویژگی‌ها، همیشه باید سعی کنید با استفاده از APIهای آن‌ها نیز یک بازه زمانی مشخص کنید. به عنوان مثال، هنگام استفاده از Guzzle، همیشه باید یک اتصال را مشخص کنید و مقدار مهلت زمانی را درخواست کنید.

پسوند pcntl PHP باید نصب شود تا زمان‌های کاری مشخص شود. به‌علاوه، مقدار «تایم‌اوت» یک کار باید همیشه کمتر از مقدار «تلاش مجدد بعد از» آن باشد . در غیر این صورت، کار ممکن است قبل از اینکه واقعاً اجرا یا به پایان برسد دوباره تلاش شود.

شکست در مهلت زمانی

اگر می‌خواهید نشان دهید که یک کار باید در زمان وقفه به‌عنوان ناموفق علامت‌گذاری شود ، می‌توانید $failOnTimeout ویژگی را در کلاس شغلی تعریف کنید:

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

رسیدگی به خطا

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

آزادسازی دستی یک شغل

گاهی اوقات ممکن است بخواهید به صورت دستی یک کار را در صف بازگردانید تا در فرصتی دیگر دوباره آن را امتحان کنید. می توانید این کار را با فراخوانی release متد انجام دهید:

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

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

$this->release(10);
 
$this->release(now()->addSeconds(10));

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

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

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

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

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

Job Batching

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

php artisan make: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.
*/
public function handle(): void
{
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),
])->before(function (Batch $batch) {
// The batch has been created but no jobs have been added...
})->progress(function (Batch $batch) {
// A single job has completed successfully...
})->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 ها استفاده کنید. علاوه بر این، از آنجایی که کارهای دسته‌ای در تراکنش‌های پایگاه داده پیچیده می‌شوند، عبارات پایگاه‌داده‌ای که commit‌های ضمنی را راه‌اندازی می‌کنند، نباید در کارها اجرا شوند.

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

برخی از ابزارها مانند 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();

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

use App\Jobs\FlushPodcastCache;
use App\Jobs\ReleasePodcast;
use App\Jobs\SendPodcastReleaseNotification;
use Illuminate\Support\Facades\Bus;
 
Bus::chain([
new FlushPodcastCache,
Bus::batch([
new ReleasePodcast(1),
new ReleasePodcast(2),
]),
Bus::batch([
new SendPodcastReleaseNotification(1),
new SendPodcastReleaseNotification(2),
]),
])->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.
*/
public function handle(): void
{
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.
*/
public function handle(): void
{
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.
*/
public function middleware(): array
{
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

use Illuminate\Support\Facades\Schedule;
 
Schedule::command('queue:prune-batches')->daily();

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

use Illuminate\Support\Facades\Schedule;
 
Schedule::command('queue:prune-batches --hours=48')->daily();

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

use Illuminate\Support\Facades\Schedule;
 
Schedule::command('queue:prune-batches --hours=48 --unfinished=72')->daily();

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

use Illuminate\Support\Facades\Schedule;
 
Schedule::command('queue:prune-batches --hours=48 --cancelled=72')->daily();

ذخیره سازی دسته ها در DynamoDB

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

به طور معمول، این جدول باید نامگذاری شود job_batches ، اما شما باید جدول را بر اساس مقدار مقدار queue.batching.table پیکربندی در فایل پیکربندی برنامه خود نامگذاری کنید queue .

پیکربندی جدول دسته ای DynamoDB

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

علاوه بر این، ttl اگر می‌خواهید از هرس دسته‌ای خودکار استفاده کنید، می‌توانید ویژگی را برای جدول خود تعریف کنید .

پیکربندی DynamoDB

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

composer require aws/aws-sdk-php

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

'batching' => [
'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' => 'job_batches',
],

هرس دسته ها در DynamoDB

هنگام استفاده از DynamoDB برای ذخیره اطلاعات دسته شغلی، دستورات هرس معمولی که برای هرس دسته های ذخیره شده در یک پایگاه داده رابطه ای استفاده می شود، کار نمی کنند. در عوض، می‌توانید از قابلیت TTL بومی DynamoDB برای حذف خودکار رکوردهای دسته‌های قدیمی استفاده کنید.

اگر جدول DynamoDB خود را با یک ویژگی تعریف کرده اید ttl ، می توانید پارامترهای پیکربندی را برای آموزش لاراول تعریف کنید که چگونه رکوردهای دسته ای را هرس کند. مقدار پیکربندی queue.batching.ttl_attribute نام ویژگی نگهدارنده TTL را مشخص می‌کند، در حالی که queue.batching.ttl مقدار پیکربندی تعداد ثانیه‌هایی را که پس از آن می‌توان یک رکورد دسته‌ای را از جدول DynamoDB حذف کرد، نسبت به آخرین باری که رکورد به‌روزرسانی شد، تعیین می‌کند:

'batching' => [
'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' => 'job_batches',
'ttl_attribute' => 'ttl',
'ttl' => 60 * 60 * 24 * 7, // 7 days...
],

بسته شدن صف

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

$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 ها استفاده کنید.

اجرای صف کارگر

دستور 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

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

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

php artisan queue:listen

در حال اجرا چند صف کارگران

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

تعیین اتصال و صف

همچنین می توانید مشخص کنید که کارگر باید از کدام اتصال صف استفاده کند. نام اتصال ارسال شده به work دستور باید با یکی از اتصالات تعریف شده در config/queue.php فایل پیکربندی شما مطابقت داشته باشد:

php artisan queue:work redis

به‌طور پیش‌فرض، این queue:work فرمان فقط وظایف صف پیش‌فرض در یک اتصال معین را پردازش می‌کند. با این حال، شما می توانید صف کارگر خود را حتی بیشتر با پردازش صف های خاص برای یک اتصال خاص سفارشی کنید. به عنوان مثال، اگر تمام ایمیل‌های شما در یک emails صف در redis اتصال صف پردازش می‌شوند، می‌توانید دستور زیر را برای راه‌اندازی کارگری که فقط آن صف را پردازش می‌کند صادر کنید:

php artisan queue:work redis --queue=emails

پردازش تعداد مشخصی از مشاغل

این --once گزینه ممکن است برای دستور دادن به کارگر برای پردازش تنها یک کار از صف استفاده شود:

php artisan queue:work --once

این --max-jobs گزینه ممکن است برای دستور دادن به کارگر برای پردازش تعداد داده شده شغل و سپس خروج استفاده شود. این گزینه ممکن است زمانی مفید باشد که با Supervisor ترکیب شود، به طوری که کارگران شما پس از پردازش تعداد معینی از کارها، به طور خودکار راه اندازی مجدد شوند و هر حافظه ای که ممکن است انباشته شده باشند، آزاد شود:

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

پردازش همه مشاغل در صف و سپس خروج

این --stop-when-empty گزینه ممکن است برای دستور دادن به کارگر برای پردازش همه مشاغل و سپس خروج با ظرافت استفاده شود. اگر بخواهید پس از خالی شدن صف، ظرف را خاموش کنید، این گزینه می‌تواند هنگام پردازش صف‌های لاراول در ظرف Docker مفید باشد:

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

پردازش مشاغل برای تعداد معینی از ثانیه

این --max-time گزینه ممکن است برای دستور دادن به کارگر برای پردازش کارها برای تعداد ثانیه معین و سپس خروج استفاده شود. این گزینه ممکن است زمانی مفید باشد که با Supervisor ترکیب شود، به طوری که کارگران شما پس از پردازش کارها برای مدت زمان معین، به طور خودکار راه اندازی مجدد شوند و هر حافظه ای را که ممکن است انباشته کرده باشند، آزاد کنند:

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

مدت زمان خواب کارگر

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

php artisan queue:work --sleep=3

حالت نگهداری و صف

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

برای اینکه کارگران صف خود را مجبور به پردازش کارها کنند حتی اگر حالت تعمیر و نگهداری فعال باشد، می توانید از --force گزینه زیر استفاده کنید:

php artisan queue:work --force

ملاحظات منابع

کارگران صف Daemon قبل از پردازش هر کار، چارچوب را "ریبوت" نمی کنند. بنابراین، پس از اتمام هر کار، باید منابع سنگین را آزاد کنید. برای مثال، اگر در حال دستکاری تصویر با کتابخانه GD هستید، باید imagedestroy پس از اتمام پردازش تصویر، حافظه را آزاد کنید .

اولویت های صف

گاهی اوقات ممکن است بخواهید نحوه پردازش صف های خود را اولویت بندی کنید. به عنوان مثال، در فایل پیکربندی خود config/queue.php ، می توانید پیش فرض اتصال queue خود را روی . با این حال، گاهی اوقات ممکن است بخواهید یک کار را به صف اولویت مانند زیر فشار دهید: redis low high

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

برای راه‌اندازی کارگری که تأیید می‌کند که همه high کارهای صف قبل از ادامه کار در صف پردازش شده‌اند low ، فهرستی از نام‌های صف با کاما را به work دستور ارسال کنید:

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

کارگران صف و استقرار

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

php artisan queue:restart

این دستور به همه کارگران صف دستور می دهد که پس از اتمام پردازش کار فعلی خود، به آرامی خارج شوند تا هیچ شغل موجود از بین نرود. از آنجایی که صف کارگران هنگام queue:restart اجرای دستور خارج می شوند، باید یک مدیر فرآیند مانند Supervisor را اجرا کنید تا به طور خودکار کارگران صف را مجددا راه اندازی کند.

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

انقضای شغل و مهلت زمانی

انقضای شغل

در فایل پیکربندی شما 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 جدول معمولاً در برنامه های جدید لاراول وجود دارد. با این حال، اگر برنامه شما حاوی یک انتقال برای این جدول نیست، می توانید از make:queue-failed-table دستور برای ایجاد مهاجرت استفاده کنید:

php artisan make: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.
*/
public function backoff(): int
{
return 3;
}

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

/**
* Calculate the number of seconds to wait before retrying the job.
*
* @return array<int, int>
*/
public function backoff(): array
{
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;
 
/**
* Create a new job instance.
*/
public function __construct(
public Podcast $podcast,
) {}
 
/**
* Execute the job.
*/
public function handle(AudioProcessor $processor): void
{
// Process uploaded podcast...
}
 
/**
* Handle a job failure.
*/
public function failed(?Throwable $exception): void
{
// 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.
*/
public function register(): void
{
// ...
}
 
/**
* Bootstrap any application services.
*/
public function boot(): void
{
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 رویداد ارسال می شود. می‌توانید به این رویداد در برنامه‌تان گوش دهید AppServiceProvider تا یک اعلان برای شما یا تیم توسعه‌تان ارسال شود:

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

آزمایش کردن

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

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

<?php
 
use App\Jobs\AnotherJob;
use App\Jobs\FinalJob;
use App\Jobs\ShipOrder;
use Illuminate\Support\Facades\Queue;
 
test('orders can be shipped', function () {
Queue::fake();
 
// Perform order shipping...
 
// Assert that no jobs were pushed...
Queue::assertNothingPushed();
 
// Assert a job was pushed to a given queue...
Queue::assertPushedOn('queue-name', ShipOrder::class);
 
// Assert a job was pushed twice...
Queue::assertPushed(ShipOrder::class, 2);
 
// Assert a job was not pushed...
Queue::assertNotPushed(AnotherJob::class);
 
// Assert that a Closure was pushed to the queue...
Queue::assertClosurePushed();
 
// Assert the total number of jobs that were pushed...
Queue::assertCount(3);
});
<?php
 
namespace Tests\Feature;
 
use App\Jobs\AnotherJob;
use App\Jobs\FinalJob;
use App\Jobs\ShipOrder;
use Illuminate\Support\Facades\Queue;
use Tests\TestCase;
 
class ExampleTest extends TestCase
{
public function test_orders_can_be_shipped(): void
{
Queue::fake();
 
// Perform order shipping...
 
// Assert that no jobs were pushed...
Queue::assertNothingPushed();
 
// Assert a job was pushed to a given queue...
Queue::assertPushedOn('queue-name', ShipOrder::class);
 
// Assert a job was pushed twice...
Queue::assertPushed(ShipOrder::class, 2);
 
// Assert a job was not pushed...
Queue::assertNotPushed(AnotherJob::class);
 
// Assert that a Closure was pushed to the queue...
Queue::assertClosurePushed();
 
// Assert the total number of jobs that were pushed...
Queue::assertCount(3);
}
}

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

Queue::assertPushed(function (ShipOrder $job) use ($order) {
return $job->order->id === $order->id;
});

جعل کردن زیرمجموعه ای از مشاغل

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

test('orders can be shipped', function () {
Queue::fake([
ShipOrder::class,
]);
 
// Perform order shipping...
 
// Assert a job was pushed twice...
Queue::assertPushed(ShipOrder::class, 2);
});
public function test_orders_can_be_shipped(): void
{
Queue::fake([
ShipOrder::class,
]);
 
// Perform order shipping...
 
// Assert a job was pushed twice...
Queue::assertPushed(ShipOrder::class, 2);
}

با استفاده از روش زیر می توانید همه کارها را جعل کنید به جز مجموعه ای از مشاغل مشخص شده except :

Queue::fake()->except([
ShipOrder::class,
]);

تست زنجیره های شغلی

برای آزمایش زنجیره های شغلی، باید از Bus قابلیت های جعلی نما استفاده کنید. روش Bus نما assertChained ممکن است برای اثبات اینکه زنجیره ای از کارها ارسال شده است استفاده شود. این assertChained روش آرایه ای از کارهای زنجیره ای را به عنوان اولین آرگومان خود می پذیرد:

use App\Jobs\RecordShipment;
use App\Jobs\ShipOrder;
use App\Jobs\UpdateInventory;
use Illuminate\Support\Facades\Bus;
 
Bus::fake();
 
// ...
 
Bus::assertChained([
ShipOrder::class,
RecordShipment::class,
UpdateInventory::class
]);

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

Bus::assertChained([
new ShipOrder,
new RecordShipment,
new UpdateInventory,
]);

می‌توانید از این assertDispatchedWithoutChain روش برای تأیید اینکه یک کار بدون زنجیره‌ای از مشاغل تحت فشار قرار گرفته است استفاده کنید:

Bus::assertDispatchedWithoutChain(ShipOrder::class);

تست دسته های زنجیر شده

اگر زنجیره شغلی شما شامل دسته‌ای از مشاغل است Bus::chainedBatch ، می‌توانید با درج یک تعریف در ادعای زنجیره‌ای خود اظهار کنید که دسته زنجیره‌ای با انتظارات شما مطابقت دارد :

use App\Jobs\ShipOrder;
use App\Jobs\UpdateInventory;
use Illuminate\Bus\PendingBatch;
use Illuminate\Support\Facades\Bus;
 
Bus::assertChained([
new ShipOrder,
Bus::chainedBatch(function (PendingBatch $batch) {
return $batch->jobs->count() === 3;
}),
new UpdateInventory,
]);

آزمایش دسته های شغلی

روش Bus نما assertBatched ممکن است برای ادعای ارسال دسته ای از کارها استفاده شود. بسته شدن داده شده به assertBatched روش نمونه ای از را دریافت می کند Illuminate\Bus\PendingBatch که ممکن است برای بازرسی کارهای درون دسته استفاده شود:

use Illuminate\Bus\PendingBatch;
use Illuminate\Support\Facades\Bus;
 
Bus::fake();
 
// ...
 
Bus::assertBatched(function (PendingBatch $batch) {
return $batch->name == 'import-csv' &&
$batch->jobs->count() === 10;
});

می‌توانید از این assertBatchCount روش برای تأیید اینکه تعداد معینی از دسته‌ها ارسال شده است استفاده کنید:

Bus::assertBatchCount(3);

می‌توانید assertNothingBatched برای تأیید اینکه هیچ دسته‌ای ارسال نشده است استفاده کنید:

Bus::assertNothingBatched();

کار تست / تعامل دسته ای

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

[$job, $batch] = (new ShipOrder)->withFakeBatch();
 
$job->handle();
 
$this->assertTrue($batch->cancelled());
$this->assertEmpty($batch->added);

تست تعاملات شغلی / صف

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

هنگامی که فعل و انفعالات صف کار جعلی شد، می توانید handle روش را در کار فراخوانی کنید. پس از فراخوانی کار، می‌توان از روش‌های assetReleased , assertDeleted و assertFailed برای اظهارنظر در برابر تعاملات صف شغل استفاده کرد:

use App\Jobs\ProcessPodcast;
 
$job = (new ProcessPodcast)->withFakeQueueInteractions();
 
$job->handle();
 
$job->assertReleased(delay: 30);
$job->assertDeleted();
$job->assertFailed();

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

با استفاده از 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.
*/
public function register(): void
{
// ...
}
 
/**
* Bootstrap any application services.
*/
public function boot(): void
{
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();
}
});