نسخه:

صف ها

معرفی

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

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

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

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

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

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

// This job is sent to the default queue...
Job::dispatch();
 
// This job is sent to the "emails" queue...
Job::dispatch()->onQueue('emails');

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

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

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

پایگاه داده

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

php artisan queue:table
 
php artisan migrate

ردیس

برای استفاده از 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 تا پردازش کار بعدی جلوگیری می‌کند.

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

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

  • آمازون 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 ایجاد می شود. می توانید با استفاده از Artisan CLI یک کار در صف جدید ایجاد کنید:

php artisan make:job ProcessPodcast

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

ساختار کلاس

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

<?php
 
namespace App\Jobs;
 
use App\AudioProcessor;
use App\Podcast;
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;
 
protected $podcast;
 
/**
* Create a new job instance.
*
* @param Podcast $podcast
* @return void
*/
public function __construct(Podcast $podcast)
{
$this->podcast = $podcast;
}
 
/**
* Execute the job.
*
* @param AudioProcessor $processor
* @return void
*/
public function handle(AudioProcessor $processor)
{
// Process uploaded podcast...
}
}

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

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

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

use App\Jobs\ProcessPodcast;
 
$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\Podcast $podcast
* @return void
*/
public function __construct(Podcast $podcast)
{
$this->podcast = $podcast->withoutRelations();
}

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

میان‌افزار شغلی به شما امکان می‌دهد منطق سفارشی را حول اجرای کارهای در صف بپیچید، و باعث کاهش حجم دیگ در خود کارها شود. به عنوان مثال، روش زیر را در نظر بگیرید handle که از ویژگی‌های محدودکننده نرخ 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];
}

اعزام مشاغل

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

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

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

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

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

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

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

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

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

زنجیره شغلی

Job chaining به شما امکان می دهد لیستی از کارهای در صف را مشخص کنید که باید پس از اجرای موفقیت آمیز کار اصلی به ترتیب اجرا شوند. اگر یک کار در دنباله شکست بخورد، بقیه کارها اجرا نمی شوند. برای اجرای یک زنجیره شغلی در صف، می توانید از این withChain روش در هر یک از کارهای قابل ارسال خود استفاده کنید:

ProcessPodcast::withChain([
new OptimizePodcast,
new ReleasePodcast
])->dispatch();

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

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

اگر می‌خواهید اتصال و صف پیش‌فرض را که باید برای کارهای زنجیره‌ای استفاده شود، مشخص کنید، می‌توانید از روش allOnConnection و استفاده کنید allOnQueue . این روش‌ها اتصال صف و نام صف را مشخص می‌کنند که باید مورد استفاده قرار گیرد، مگر اینکه به کار در صف صریحاً یک اتصال / صف متفاوت اختصاص داده شود:

ProcessPodcast::withChain([
new OptimizePodcast,
new ReleasePodcast
])->dispatch()->allOnConnection('redis')->allOnQueue('podcasts');

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

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

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

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

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

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

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

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

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

همچنین، می‌توانید connection ویژگی را در کلاس شغلی مشخص کنید:

<?php
 
namespace App\Jobs;
 
class ProcessPodcast implements ShouldQueue
{
/**
* The queue connection that should handle the job.
*
* @var string
*/
public $connection = 'sqs';
}

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

حداکثر تلاش

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

php artisan queue:work --tries=3

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

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

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

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

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

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

تایم اوت

این timeout ویژگی برای PHP 7.1+ و pcntl پسوند PHP بهینه شده است.

به همین ترتیب، حداکثر تعداد ثانیه هایی که کارها می توانند اجرا شوند را می توان با استفاده از --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;
}

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

این ویژگی مستلزم آن است که برنامه شما بتواند با یک سرور Redis تعامل داشته باشد .

اگر برنامه شما با Redis تعامل داشته باشد، ممکن است مشاغل در صف خود را با زمان یا همزمان کاهش دهید. این ویژگی می تواند کمک کننده باشد زمانی که مشاغل در صف شما در حال تعامل با APIهایی هستند که دارای نرخ محدود نیز هستند.

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

Redis::throttle('key')->allow(10)->every(60)->then(function () {
// Job logic...
}, function () {
// Could not obtain lock...
 
return $this->release(10);
});

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

بازگرداندن یک کار throttled دوباره در صف همچنان تعداد کل کار را افزایش می دهد attempts .

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

Redis::funnel('key')->limit(1)->then(function () {
// Job logic...
}, function () {
// Could not obtain lock...
 
return $this->release(10);
});

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

رسیدگی به خطا

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

بسته شدن صف

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

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

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

اجرای The Queue Worker

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

php artisan queue:work

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

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

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

php artisan queue:listen

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

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

php artisan queue:work redis

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

php artisan queue:work redis --queue=emails

پردازش یک کار واحد

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

php artisan queue:work --once

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

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

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

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

کارگران صف 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 گزینه مشخص می‌کند که پردازش اصلی صف لاراول قبل از کشتن یک کارگر صف که در حال پردازش یک کار است، چه مدت منتظر بماند. گاهی اوقات یک فرآیند صف کودک می تواند به دلایل مختلف "یخ زده" شود. این --timeout گزینه فرآیندهای منجمد شده را که از محدودیت زمانی مشخص شده فراتر رفته اند حذف می کند:

php artisan queue:work --timeout=60

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

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

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

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

php artisan queue:work --sleep=3

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

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

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
autostart=true
autorestart=true
user=forge
numprocs=8
redirect_stderr=true
stdout_logfile=/home/forge/app.com/worker.log
stopwaitsecs=3600

در این مثال، numprocs دستورالعمل به Supervisor دستور می‌دهد که 8 فرآیند را اجرا کند queue:work و همه آنها را نظارت کند و در صورت شکست، آنها را به طور خودکار راه‌اندازی مجدد کند. شما باید queue:work sqs بخشی از 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 ، کارها فقط یک بار انجام خواهند شد:

php artisan queue:work redis --tries=3

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

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

اگر می‌خواهید تاخیر امتحان مجدد کار ناموفق را بر اساس هر شغل پیکربندی کنید، می‌توانید این کار را با تعریف یک retryAfter ویژگی در کلاس شغلی در صف خود انجام دهید:

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

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

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

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

failed اگر کار با استفاده از متد ارسال شده باشد، متد فراخوانی نخواهد شد dispatchNow .

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

اگر می‌خواهید رویدادی را ثبت کنید که در صورت شکست یک کار فراخوانی شود، می‌توانید از این Queue::failing روش استفاده کنید. این رویداد یک فرصت عالی برای اطلاع تیم خود از طریق ایمیل یا Slack است . به عنوان مثال، ممکن است یک callback از رویدادی 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
});
}
}

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

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

php artisan queue:failed

این queue:failed دستور شناسه شغل، اتصال، صف و زمان شکست را فهرست می کند. شناسه شغل ممکن است برای امتحان مجدد کار ناموفق استفاده شود. به عنوان مثال، برای امتحان مجدد یک کار ناموفق که دارای شناسه است 5 ، دستور زیر را صادر کنید:

php artisan queue:retry 5

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

php artisan queue:retry all

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

php artisan queue:forget 5

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

php artisan queue:flush

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

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

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

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

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

با استفاده از before و after متدهای روی Queue نما ، می‌توانید فراخوان‌هایی را مشخص کنید که قبل یا بعد از پردازش یک کار در صف اجرا شوند. این فراخوان ها فرصتی عالی برای انجام ثبت گزارش یا آمار افزایشی برای داشبورد هستند. به طور معمول، شما باید این روش ها را از یک ارائه دهنده خدمات فراخوانی کنید . به عنوان مثال، ممکن است از چیزی 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 نما ، می‌توانید فراخوان‌هایی را مشخص کنید که قبل از تلاش کارگر برای واکشی کاری از یک صف اجرا شوند. به عنوان مثال، ممکن است برای بازگرداندن تراکنش‌هایی که توسط یک کار شکست خورده قبلی باز مانده‌اند، یک بستن ثبت کنید:

Queue::looping(function () {
while (DB::transactionLevel() > 0) {
DB::rollBack();
}
});