Kolejki i zadania w Laravel: architektura asynchroniczna i pytania rekrutacyjne 2026
Dogłębne omówienie architektury kolejek i zadań w Laravel. Obejmuje dispatching, batching, łańcuchowanie, middleware, obsługę nieudanych zadań i zarządzanie workerami z przykładami Laravel 12.

Kolejki w Laravel oferują ujednolicone API do odraczania czasochłonnych zadań — wysyłki maili, przetwarzania uploadów, generowania raportów — do procesów wykonywanych w tle. Zamiast zmuszać użytkowników do oczekiwania, aplikacja wrzuca zadanie do kolejki i kontynuuje pracę. Mechanizm ten stanowi rdzeń każdej skalowalnej aplikacji Laravel.
Laravel obsługuje wiele backendów kolejek (Redis, Amazon SQS, baza danych, Beanstalkd) poprzez jedno, niezależne od sterownika API. Zadania to serializowane klasy PHP implementujące interfejs ShouldQueue. Workery pobierają zadania z kolejki, deserializują je i wykonują metodę handle(). Nieudane zadania trafiają do dedykowanej tabeli failed_jobs, gdzie czekają na ponowienie lub analizę.
Jak działa dispatching zadań w Laravel pod maską
Wywołanie dispatch() na klasie zadania powoduje, że Laravel serializuje instancję — wraz z jej publicznymi właściwościami — i wrzuca payload do skonfigurowanego połączenia kolejki. Serializowany payload zawiera w pełni kwalifikowaną nazwę klasy, zserializowane właściwości, nazwę docelowej kolejki oraz metadane, takie jak liczba dozwolonych prób i timeout.
Proces workera (php artisan queue:work) działa jako długo żyjący demon, który odpytuje backend kolejki w poszukiwaniu nowych zadań. Po odebraniu payloadu worker deserializuje zadanie, rozwiązuje jego zależności poprzez kontener usług i wywołuje 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(),
]);
}
}Trait SerializesModels przechowuje wyłącznie klucz główny modelu i jego nazwę klasy, a nie cały model Eloquent. Gdy worker przetwarza zadanie, pobiera świeży model z bazy danych. Takie podejście zapobiega pracy na nieaktualnych danych i utrzymuje rozmiar payloadu na niskim poziomie.
Batching zadań dla równoległych obciążeń
Batching grupuje wiele zadań w jeden batch, śledzi ich łączny postęp i wyzwala callbacki po zakończeniu wszystkich zadań — albo gdy którekolwiek z nich zakończy się błędem. Ten wzorzec sprawdza się przy importach danych, masowych powiadomieniach i generowaniu raportów, gdzie kilka niezależnych jednostek pracy musi się zakończyć zanim wystartuje krok finalny.
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 wzbogaca payloady batchy o metadane, w tym czas oczekiwania w kolejce i identyfikację workera. Metoda allowFailures() zapobiega anulowaniu całego batcha przez pojedyncze nieudane zadanie — kluczowe przy dużych importach, gdzie częściowy sukces jest akceptowalny.
Łańcuchy zadań dla sekwencyjnych przepływów
Podczas gdy batching obsługuje obciążenia równoległe, łańcuchowanie gwarantuje wykonanie sekwencyjne. Każde zadanie w łańcuchu uruchamia się dopiero po pomyślnym zakończeniu poprzedniego. W razie błędu pozostała część łańcucha zostaje porzucona, a callback catch zostaje uruchomiony.
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();
}Łańcuchowanie wyróżnia się w przepływach domenowych, w których kolejność kroków ma znaczenie — płatność musi zostać zwalidowana zanim zarezerwowany zostanie magazyn, a etykiety wysyłkowe zależą od potwierdzonego stanu magazynowego.
Gotowy na rozmowy o Laravel?
Ćwicz z naszymi interaktywnymi symulatorami, flashcards i testami technicznymi.
Middleware kolejek dla zagadnień przekrojowych
Middleware kolejek otacza wykonanie zadania reużywalną logiką: rate limiting, deduplikacja, circuit breaker. Zamiast wbijać te zagadnienia w każde zadanie, middleware utrzymuje zadania skupione na logice biznesowej.
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 podpina się poprzez zdefiniowanie metody middleware() na klasie zadania:
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 wykorzystuje atomowe locki, aby tylko jedna instancja zadania (identyfikowana po kluczu) działała w danym momencie. W połączeniu z rate limitingiem zapobiega zarówno duplikacji przetwarzania, jak i throttlingowi po stronie API.
Obsługa nieudanych zadań i strategie ponawiania
Produkcyjne systemy kolejek wymagają solidnej obsługi błędów. Laravel zapisuje nieudane zadania w tabeli failed_jobs razem z pełnym payloadem, śladem wyjątku oraz informacją o kolejce/połączeniu, które wytworzyło błąd. Metoda failed() na klasie zadania uruchamia się po wyczerpaniu wszystkich prób.
Konfigurowanie zachowania ponawiania per zadanie daje precyzyjną kontrolę:
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));
}
}Rozróżnienie między $tries, $maxExceptions i retryUntil() ma znaczenie podczas rozmów rekrutacyjnych. $tries liczy każdą próbę, w tym ręczne wywołania release. $maxExceptions zlicza wyłącznie nieobsłużone wyjątki. retryUntil() ustawia okno czasowe niezależne od liczby prób.
Zarządzanie workerami i deployment
Workery kolejek na produkcji wymagają nadzoru procesów, łagodnego restartu podczas deploymentu i zarządzania zasobami. Supervisor jest standardowym narzędziem utrzymującym workery przy życiu.
; /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=trueKluczowe aspekty deploymentu:
- Łagodny restart:
php artisan queue:restartsygnalizuje workerom zakończenie aktualnego zadania przed restartem. Zapobiega to korupcji zadań podczas deployu. - Maks. czas/zadań:
--max-time=3600i--max-jobs=1000zapobiegają wyciekom pamięci poprzez okresowe recyklowanie procesów workera. - Interwał uśpienia:
--sleep=3kontroluje czas oczekiwania workera przed kolejnym odpytaniem pustej kolejki. Niższe wartości zwiększają responsywność, ale również obciążają bazę/Redis. - Wiele kolejek:
--queue=critical,default,lowprzetwarza kolejki w kolejności priorytetowej. Workery opróżniają kolejkęcriticalzanim sięgną podefault.
Laravel 12.37 wprowadził połączenie kolejki background, które odracza zadania używając Concurrently::defer(). Ten driver serializuje i uruchamia zadanie w osobnym procesie PHP — przydatne dla lekkich zadań, które nie uzasadniają pełnej infrastruktury kolejkowej.
Unikalne zadania i szyfrowane payloady
Dwa wzorce regularnie pojawiają się w produkcji oraz na rozmowach rekrutacyjnych: zapewnienie, że zadanie wykona się tylko raz dla danego klucza, oraz ochrona wrażliwych danych w payloadach zadań.
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);
}
}Dla zadań zawierających wrażliwe dane (dane logowania, tokeny płatności) interfejs ShouldBeEncrypted szyfruje cały serializowany payload w stanie spoczynku:
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 jest szyfrowany kluczem aplikacji przed zapisem w Redis lub bazie danych. Workery automatycznie deszyfrują go przed deserializacją.
Najczęstsze pytania rekrutacyjne dotyczące kolejek Laravel
Rozmowy techniczne często sprawdzają zrozumienie architektury kolejek wykraczające poza znajomość API.
Co się dzieje, gdy zakolejkowane zadanie odwołuje się do usuniętego modelu Eloquent?
Z trait SerializesModels worker próbuje pobrać model po ID podczas przetwarzania zadania. Jeśli model już nie istnieje, Laravel rzuca ModelNotFoundException. Aby obsłużyć to płynnie, należy ustawić właściwość $deleteWhenMissingModels na true — zadanie zostanie po cichu usunięte z kolejki zamiast zakończyć się błędem.
Czym ShouldBeUnique różni się od middleware WithoutOverlapping?
ShouldBeUnique zapobiega dispatchowaniu zadania, jeśli inne z tym samym kluczem unikalnym już istnieje w kolejce. WithoutOverlapping pozwala na dispatch, ale uniemożliwia równoczesne wykonanie — jeśli zadanie z tym samym kluczem już działa, nowa instancja zostaje zwrócona do kolejki. Rozwiązują różne problemy i mogą być łączone.
Kiedy retryUntil() powinno być preferowane nad $tries?
retryUntil() sprawdza się dla zadań, które komunikują się z usługami zewnętrznymi, gdzie czas odzyskania jest nieprzewidywalny. Stała liczba ponowień ($tries = 3) może wyczerpać próby podczas krótkiej awarii. retryUntil() ustawia okno czasowe (np. 24 godziny) i kontynuuje ponawianie z backoffem, dopóki usługa nie wróci lub okno nie wygaśnie.
Jak działają priorytety kolejek z wieloma kolejkami?
Uruchomienie queue:work --queue=critical,default,low tworzy system priorytetów. Worker w pełni opróżnia kolejkę critical zanim sprawdzi default, a default zanim low. Oznacza to, że zadania o niskim priorytecie mogą głodować w okresach szczytu. Dla rygorystycznych SLA dedykowane workery per kolejka dają lepsze gwarancje.
Zacznij ćwiczyć!
Sprawdź swoją wiedzę z naszymi symulatorami rozmów i testami technicznymi.
Podsumowanie
- Kolejki Laravel abstrakują backend kolejki za niezależnym od sterownika API obsługującym Redis, SQS, bazę danych oraz nowe połączenie
backgroundw Laravel 12.37 - Batching zadań obsługuje obciążenia równoległe ze śledzeniem zbiorczego postępu, podczas gdy łańcuchowanie wymusza sekwencyjne wykonanie dla przepływów domenowych
- Middleware kolejek (rate limiting,
WithoutOverlapping) trzyma zagadnienia przekrojowe poza logiką biznesową zadań - Obsługa nieudanych zadań łączy
$tries,$maxExceptions,retryUntil()oraz wykładniczy backoff dla odpornych strategii ponawiania ShouldBeUniquezapobiega duplikacji dispatchu;ShouldBeEncryptedchroni wrażliwe payloady w stanie spoczynku- Produkcyjne workery wymagają Supervisora, łagodnych restartów podczas deployów oraz zarządzania pamięcią poprzez
--max-timei--max-jobs - Przygotowanie do rekrutacji powinno obejmować rozróżnienie między unikalnością na etapie dispatchu i lockowaniem na etapie wykonania, zachowanie serializacji modeli oraz głodzenie kolejek priorytetowych
Aby ćwiczyć pytania rekrutacyjne Laravel, bank pytań SharpSkill obejmuje kolejki, middleware oraz wzorce Eloquent ze szczegółowymi wyjaśnieniami.
Zacznij ćwiczyć!
Sprawdź swoją wiedzę z naszymi symulatorami rozmów i testami technicznymi.
Tagi
Udostępnij
Powiązane artykuły

Laravel Middleware od podstaw: Uwierzytelnianie, Rate Limiting i własne middleware
Kompleksowy przewodnik po middleware w Laravel - od uwierzytelniania i rate limitingu po tworzenie własnych klas middleware. Praktyczne przykłady kodu i wzorce produkcyjne.

Eloquent ORM: wzorce i optymalizacje dla Laravela
Opanuj Eloquent ORM dzięki zaawansowanym wzorcom i technikom optymalizacji. Eager loading, query scopes, accessory, mutatory i wydajność dla aplikacji Laravel.

25 pytań rekrutacyjnych z Laravel i PHP w 2026 roku
25 najczęściej zadawanych pytań rekrutacyjnych z Laravel: Service Container, Eloquent ORM, middleware, kolejki, bezpieczeństwo, testowanie i wzorce architektoniczne z przykładami kodu.