Черги та завдання в Laravel: асинхронна архітектура та питання на співбесідах 2026
Глибоке занурення в архітектуру черг і завдань Laravel. Охоплює диспетчеризацію, батчинг, ланцюги, middleware, обробку невдалих завдань і керування воркерами з прикладами Laravel 12.

Черги в Laravel надають уніфікований API для відкладання трудомістких завдань — надсилання листів, обробки завантажень, генерації звітів — у фонових воркерів. Замість того щоб змушувати користувачів чекати, застосунок надсилає завдання в чергу і продовжує роботу. Цей механізм лежить в основі будь-якого масштабованого Laravel-застосунку.
Laravel підтримує кілька бекендів черг (Redis, Amazon SQS, базу даних, Beanstalkd) через єдиний API, що не залежить від драйвера. Завдання — це серіалізовані PHP-класи, які реалізують інтерфейс ShouldQueue. Воркери витягують завдання з черги, десеріалізують їх і виконують метод handle(). Завдання, що не виконалися, потрапляють у спеціальну таблицю failed_jobs для повторних спроб або аналізу.
Як працює диспетчеризація завдань Laravel під капотом
Коли на класі завдання викликається dispatch(), Laravel серіалізує екземпляр завдання — разом із його публічними властивостями — і надсилає payload у налаштоване підключення черги. Серіалізований payload містить повну назву класу, серіалізовані властивості, назву цільової черги та метадані, такі як кількість дозволених спроб і таймаут.
Процес воркера (php artisan queue:work) працює як довготривалий демон, який опитує бекенд черги про нові завдання. Отримавши payload, воркер десеріалізує завдання, розв'язує його залежності через сервіс-контейнер та викликає handle().
namespace App\Jobs;
use App\Models\Order;
use App\Services\PdfGenerator;
use Illuminate\Bus\Queueable;
use Illuminate\Contracts\Queue\ShouldQueue;
use Illuminate\Foundation\Bus\Dispatchable;
use Illuminate\Queue\InteractsWithQueue;
use Illuminate\Queue\SerializesModels;
class ProcessInvoice implements ShouldQueue
{
use Dispatchable, InteractsWithQueue, Queueable, SerializesModels;
public int $tries = 3;
public int $backoff = 60;
public int $timeout = 120;
public function __construct(
public readonly Order $order
) {}
public function handle(PdfGenerator $pdf): void
{
// Generate PDF invoice for the order
$invoice = $pdf->generate($this->order);
// Store the generated file
$this->order->update([
'invoice_path' => $invoice->path(),
'invoiced_at' => now(),
]);
}
public function failed(\Throwable $e): void
{
// Notify the ops team when invoice generation fails
logger()->error('Invoice generation failed', [
'order_id' => $this->order->id,
'error' => $e->getMessage(),
]);
}
}Трейт SerializesModels зберігає лише первинний ключ моделі та назву її класу, а не всю Eloquent-модель. Коли воркер обробляє завдання, він підтягує свіжу модель з бази даних. Це запобігає роботі зі застарілими даними та утримує розмір payload невеликим.
Батчинг завдань для паралельних навантажень
Батчинг групує кілька завдань в один батч, відстежує їхній спільний прогрес та запускає колбеки після завершення всіх завдань — або коли якесь із них зазнає невдачі. Цей патерн підходить для імпорту даних, масових сповіщень і генерації звітів, де кілька незалежних одиниць роботи мають завершитися перед запуском фінального кроку.
use App\Jobs\ImportRow;
use Illuminate\Bus\Batch;
use Illuminate\Support\Facades\Bus;
public function import(Request $request)
{
$rows = $this->parseCSV($request->file('data'));
// Create a batch of import jobs, one per CSV row
$batch = Bus::batch(
collect($rows)->map(fn ($row) => new ImportRow($row))
)
->then(function (Batch $batch) {
// All jobs completed successfully
Notification::send(
auth()->user(),
new ImportComplete($batch->totalJobs)
);
})
->catch(function (Batch $batch, \Throwable $e) {
// First failure in the batch
logger()->warning('Batch import partial failure', [
'batch_id' => $batch->id,
'failed' => $batch->failedJobs,
]);
})
->finally(function (Batch $batch) {
// Runs after all jobs finish (success or failure)
Cache::forget("import_lock_{$batch->id}");
})
->allowFailures()
->dispatch();
return response()->json(['batch_id' => $batch->id]);
}Laravel 12 збагачує payload'и батчів метаданими, включно з часом очікування в черзі та ідентифікацією воркера. Метод allowFailures() запобігає скасуванню всього батча через одне невдале завдання — критично для великих імпортів, де часткова успішність прийнятна.
Ланцюги завдань для послідовних робочих процесів
Тоді як батчинг обробляє паралельні навантаження, ланцюги гарантують послідовне виконання. Кожне завдання в ланцюгу запускається лише після успішного завершення попереднього. Якщо якесь завдання зазнає невдачі, решта ланцюга відкидається, а колбек catch спрацьовує.
use App\Jobs\ValidatePayment;
use App\Jobs\ReserveInventory;
use App\Jobs\SendConfirmation;
use App\Jobs\GenerateShippingLabel;
use Illuminate\Support\Facades\Bus;
public function processOrder(Order $order): void
{
// Each job runs only after the previous one succeeds
Bus::chain([
new ValidatePayment($order),
new ReserveInventory($order),
new GenerateShippingLabel($order),
new SendConfirmation($order),
])
->onQueue('orders')
->catch(function (\Throwable $e) use ($order) {
// Roll back the order if any step fails
$order->update(['status' => 'failed']);
logger()->error('Order chain failed', [
'order_id' => $order->id,
'step' => $e->getMessage(),
]);
})
->dispatch();
}Ланцюги добре працюють для доменних робочих процесів, де порядок кроків важливий — оплата має бути валідована перед резервуванням запасів, а транспортні етикетки залежать від підтверджених запасів.
Готовий до співбесід з Laravel?
Практикуйся з нашими інтерактивними симуляторами, flashcards та технічними тестами.
Middleware черг для наскрізних аспектів
Middleware черги обгортає виконання завдання повторно використовуваною логікою: rate limiting, дедуплікація, circuit breaker. Замість того щоб вбудовувати ці аспекти в кожне завдання, middleware утримує завдання сфокусованими на бізнес-логіці.
namespace App\Jobs\Middleware;
use Closure;
use Illuminate\Support\Facades\RateLimiter;
class RateLimitedJob
{
public function __construct(
private string $key,
private int $maxAttempts = 10,
private int $decaySeconds = 60
) {}
public function handle(object $job, Closure $next): void
{
// Release job back to queue if rate limit exceeded
if (RateLimiter::tooManyAttempts($this->key, $this->maxAttempts)) {
$job->release($this->decaySeconds);
return;
}
RateLimiter::hit($this->key, $this->decaySeconds);
$next($job);
}
}Middleware застосовується через визначення методу middleware() на класі завдання:
public function middleware(): array
{
return [
new RateLimitedJob(
key: 'external-api',
maxAttempts: 30,
decaySeconds: 60
),
// Prevent duplicate jobs from running concurrently
(new WithoutOverlapping($this->apiResource->id))
->releaseAfter(300)
->expireAfter(600),
];
}Middleware WithoutOverlapping використовує атомарні блокування, щоб лише один екземпляр завдання (визначений ключем) виконувався одночасно. У поєднанні з rate limiting це запобігає як дублюванню обробки, так і throttling зовнішнього API.
Обробка невдалих завдань і стратегії повторних спроб
Виробничі системи черг потребують надійної обробки помилок. Laravel зберігає невдалі завдання в таблиці failed_jobs разом з повним payload, трасуванням винятку та чергою/підключенням, які спричинили збій. Метод failed() на класі завдання запускається після вичерпання всіх спроб повтору.
Налаштування поведінки повторних спроб для кожного завдання дає тонкий контроль:
class SyncExternalData implements ShouldQueue
{
use Dispatchable, InteractsWithQueue, Queueable, SerializesModels;
public int $tries = 5;
// Exponential backoff: 10s, 30s, 60s, 120s, 300s
public function backoff(): array
{
return [10, 30, 60, 120, 300];
}
// Job-specific timeout
public int $timeout = 180;
// Maximum exceptions before marking as failed
public int $maxExceptions = 3;
public function retryUntil(): \DateTime
{
// Keep retrying for up to 24 hours
return now()->addHours(24);
}
public function handle(): void
{
$response = Http::timeout(30)
->retry(2, 1000)
->get('https://api.vendor.com/data');
if ($response->failed()) {
// Release back to queue with delay for transient failures
$this->release(60);
return;
}
DataSync::process($response->json());
}
public function failed(\Throwable $e): void
{
Notification::route('slack', config('services.slack.ops_channel'))
->notify(new SyncFailed($e));
}
}Різниця між $tries, $maxExceptions та retryUntil() має значення на співбесідах. $tries рахує кожну спробу, включно з ручними викликами release. $maxExceptions рахує лише непокриті винятки. retryUntil() встановлює часове вікно незалежно від кількості спроб.
Управління воркерами черг і деплоймент
Воркери черг у продакшені вимагають нагляду за процесами, делікатних рестартів під час деплою та керування ресурсами. Supervisor — стандартний інструмент для підтримки воркерів живими.
; /etc/supervisor/conf.d/laravel-worker.conf
[program:laravel-worker]
process_name=%(program_name)s_%(process_num)02d
command=php /var/www/app/artisan queue:work redis --sleep=3 --tries=3 --max-time=3600
autostart=true
autorestart=true
stopwaitsecs=3600
user=www-data
numprocs=4
redirect_stderr=true
stdout_logfile=/var/log/worker.log
stopasgroup=true
killasgroup=trueКлючові міркування деплою:
- Делікатний рестарт:
php artisan queue:restartсигналізує воркерам завершити поточне завдання перед перезапуском. Це запобігає пошкодженню завдань під час деплою. - Максимальний час/завдань:
--max-time=3600і--max-jobs=1000запобігають витокам пам'яті за рахунок періодичного перезапуску процесів воркера. - Інтервал сну:
--sleep=3контролює час очікування воркера перед повторним опитуванням порожньої черги. Менші значення підвищують реактивність, але також збільшують навантаження на базу/Redis. - Кілька черг:
--queue=critical,default,lowобробляє черги в порядку пріоритету. Воркери спорожняють чергуcriticalперш ніж торкнутисяdefault.
Laravel 12.37 представив підключення черги background, яке відкладає завдання за допомогою Concurrently::defer(). Цей драйвер серіалізує і запускає завдання в окремому процесі PHP — корисно для легких завдань, що не виправдовують повноцінну інфраструктуру черг.
Унікальні завдання та зашифровані payload'и
Два патерни часто з'являються в продакшені та обговореннях на співбесідах: гарантія того, що завдання виконується лише раз для заданого ключа, і захист чутливих даних у payload завдань.
use Illuminate\Contracts\Queue\ShouldBeUnique;
class RebuildSearchIndex implements ShouldQueue, ShouldBeUnique
{
// Lock duration in seconds
public int $uniqueFor = 3600;
public function __construct(
public readonly string $indexName
) {}
// Unique key scopes the lock to this specific index
public function uniqueId(): string
{
return $this->indexName;
}
public function handle(): void
{
SearchIndex::rebuild($this->indexName);
}
}Для завдань, що несуть чутливі дані (облікові дані користувача, платіжні токени), інтерфейс ShouldBeEncrypted шифрує весь серіалізований payload у стані спокою:
use Illuminate\Contracts\Queue\ShouldBeEncrypted;
class ProcessPayment implements ShouldQueue, ShouldBeEncrypted
{
public function __construct(
private string $paymentToken,
private float $amount
) {}
public function handle(PaymentGateway $gateway): void
{
$gateway->charge($this->paymentToken, $this->amount);
}
}Payload шифрується ключем застосунку перед збереженням у Redis або базі даних. Воркери автоматично розшифровують його перед десеріалізацією.
Поширені питання на співбесідах щодо черг Laravel
Технічні співбесіди часто перевіряють розуміння архітектури черг за межами поверхневих знань API.
Що відбувається, коли завдання в черзі посилається на видалену Eloquent-модель?
З SerializesModels воркер намагається підтягнути модель за ID під час обробки завдання. Якщо моделі більше не існує, Laravel кидає ModelNotFoundException. Щоб обробити це м'яко, потрібно встановити властивість $deleteWhenMissingModels у true — завдання тихо видалиться з черги замість того, щоб впасти.
Чим ShouldBeUnique відрізняється від middleware WithoutOverlapping?
ShouldBeUnique запобігає диспетчеризації завдання, якщо інше з тим самим унікальним ключем уже існує в черзі. WithoutOverlapping дозволяє диспетчеризацію, але запобігає одночасному виконанню — якщо завдання з тим самим ключем уже виконується, новий екземпляр повертається в чергу. Вони вирішують різні проблеми і можуть комбінуватися.
Коли слід надати перевагу retryUntil() над $tries?
retryUntil() підходить для завдань, які взаємодіють зі зовнішніми сервісами, де час відновлення непередбачуваний. Фіксована кількість спроб ($tries = 3) може вичерпатися під час короткого збою. retryUntil() встановлює часове вікно (наприклад, 24 години) і продовжує повторювати з backoff, поки сервіс не відновиться або вікно не закінчиться.
Як працюють пріоритети черг із кількома чергами?
Запуск queue:work --queue=critical,default,low створює систему пріоритетів. Воркер повністю спорожняє чергу critical перед перевіркою default, і default перед low. Це означає, що низькопріоритетні завдання можуть голодувати під піковим навантаженням. Для суворих SLA окремі воркери на чергу дають кращі гарантії.
Починай практикувати!
Перевір свої знання з нашими симуляторами співбесід та технічними тестами.
Висновок
- Черги Laravel абстрагують бекенд черги за API, незалежним від драйвера, який підтримує Redis, SQS, базу даних і нове підключення
backgroundу Laravel 12.37 - Батчинг завдань обробляє паралельні навантаження з відстеженням спільного прогресу, тоді як ланцюги забезпечують послідовне виконання для доменних робочих процесів
- Middleware черг (rate limiting,
WithoutOverlapping) утримує наскрізні аспекти поза бізнес-логікою завдань - Обробка невдалих завдань поєднує
$tries,$maxExceptions,retryUntil()і експоненційний backoff для стійких стратегій повторних спроб ShouldBeUniqueзапобігає дублюванню диспетчеризації;ShouldBeEncryptedзахищає чутливі payload'и в стані спокою- Виробничі воркери вимагають Supervisor, делікатних рестартів під час деплоїв і керування пам'яттю через
--max-timeта--max-jobs - Підготовка до співбесід має охоплювати різницю між унікальністю на етапі диспетчеризації та блокуванням на етапі виконання, поведінку серіалізації моделей та голодування пріоритетних черг
Для практики з питаннями на співбесідах з Laravel банк питань SharpSkill охоплює черги, middleware та патерни Eloquent з детальними поясненнями.
Починай практикувати!
Перевір свої знання з нашими симуляторами співбесід та технічними тестами.
Теги
Поділитися
Пов'язані статті

Laravel Middleware: Поглиблений огляд автентифікації, Rate Limiting та власних middleware
Вичерпний посібник з middleware у Laravel — від автентифікації та rate limiting до створення власних класів middleware. Практичні приклади коду та патерни для продакшену.

Eloquent ORM: патерни та оптимізації для Laravel
Опануйте Eloquent ORM завдяки просунутим патернам і технікам оптимізації. Eager loading, query scopes, accessors, mutators та продуктивність для Laravel-застосунків.

25 запитань на співбесіді з Laravel та PHP у 2026 році
25 найпоширеніших запитань на співбесіді з Laravel: Service Container, Eloquent ORM, middleware, черги, безпека, тестування та архітектурні патерни з прикладами коду.