Laravel Queues en Jobs: Asynchrone Architectuur en Interviewvragen 2026

Leer hoe Laravel queues, job batching, chaining en retry-strategieën werken. Bereid je voor op technische interviews met veelgestelde vragen over queue-architectuur.

Diagram van Laravel queue-architectuur met job dispatching, batching en chaining workflows

Laravel biedt een krachtig queue-systeem waarmee tijdrovende taken -- zoals het genereren van PDF-facturen, het versturen van e-mails of het synchroniseren van externe data -- asynchroon verwerkt worden. In plaats van gebruikers te laten wachten tot zware processen zijn afgerond, worden deze taken op de achtergrond uitgevoerd via jobs en queues. Dit architectuurpatroon verbetert niet alleen de responsiviteit van webapplicaties, maar maakt ook horizontale schaalbaarheid mogelijk. Voor ontwikkelaars die zich voorbereiden op technische sollicitatiegesprekken in 2026 is een grondige kennis van Laravel queues en jobs onmisbaar geworden.

Laravel's queue-architectuur ontkoppelt tijdrovende processen van het HTTP-request/response-cycle. Dit patroon is fundamenteel voor schaalbare applicaties en komt vrijwel altijd aan bod tijdens technische interviews voor Laravel-posities.

Hoe Job Dispatching werkt in Laravel

Het fundament van het queue-systeem is de ShouldQueue interface. Door deze interface te implementeren geeft een job aan dat deze asynchroon verwerkt moet worden in plaats van synchroon binnen het huidige request. Laravel serialiseert de job-payload en plaatst deze op de geconfigureerde queue-driver (Redis, Amazon SQS, database of Beanstalkd).

Een veelvoorkomend voorbeeld is het genereren van facturen na een bestelling. De job ontvangt een Eloquent-model, configureert retry-logica en definieert faalafhandeling:

App/Jobs/ProcessInvoice.phpphp
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(),
        ]);
    }
}

De SerializesModels trait zorgt ervoor dat alleen de identifier van het Eloquent-model wordt geserialiseerd. Bij het verwerken van de job haalt Laravel het model opnieuw op uit de database, waardoor altijd met actuele data gewerkt wordt. De properties $tries, $backoff en $timeout bieden fijnmazige controle over het retry-gedrag.

Job Batching voor parallelle werklasten

Wanneer een groot aantal onafhankelijke taken tegelijkertijd uitgevoerd moet worden, biedt Laravel's batch-systeem een elegante oplossing. Met Bus::batch() worden meerdere jobs gegroepeerd, waarna callbacks gedefinieerd worden voor succesvolle afronding, falen en voltooiing. Dit is bijzonder nuttig voor bulk-imports, massa-e-mailverzendingen of data-migraties.

App/Http/Controllers/ImportController.phpphp
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]);
}

De allowFailures()-methode voorkomt dat de gehele batch wordt geannuleerd wanneer individuele jobs falen. De voortgang van een batch kan in real-time gevolgd worden via $batch->progress(), wat de implementatie van voortgangsbalken in de frontend vergemakkelijkt.

Job Chaining voor sequentiële workflows

In tegenstelling tot batching -- waarbij jobs parallel worden uitgevoerd -- garandeert job chaining dat taken in een strikte volgorde worden afgehandeld. Elke job in de keten start pas nadat de voorgaande succesvol is afgerond. Dit patroon is essentieel voor workflows waarbij de uitkomst van de ene stap de invoer vormt voor de volgende.

App/Services/OrderWorkflow.phpphp
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();
}

Wanneer een stap in de keten faalt, wordt de catch-callback uitgevoerd en worden de resterende jobs niet meer verwerkt. Dit voorkomt inconsistente toestanden in het systeem. De onQueue('orders')-aanroep stuurt de gehele keten naar een specifieke queue, waardoor prioriteitsbeheer mogelijk wordt.

Klaar om je Laravel gesprekken te halen?

Oefen met onze interactieve simulatoren, flashcards en technische tests.

Queue Middleware voor cross-cutting concerns

Laravel ondersteunt middleware op job-niveau, vergelijkbaar met HTTP-middleware. Dit maakt het mogelijk om herbruikbare logica toe te passen op jobs zonder de business logic te vervuilen. Veelvoorkomende toepassingen zijn rate limiting, het voorkomen van overlappende uitvoering en circuit breaking.

App/Jobs/Middleware/RateLimitedJob.phpphp
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);
    }
}

De toepassing van deze middleware in een job die een externe API aanroept, gecombineerd met WithoutOverlapping om dubbele uitvoering te voorkomen:

App/Jobs/CallExternalApi.phpphp
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),
    ];
}

De WithoutOverlapping-middleware gebruikt een atomaire lock om te garanderen dat slechts één job tegelijk voor dezelfde resource draait. Dit is cruciaal bij het werken met externe systemen die geen idempotente operaties ondersteunen.

Faalafhandeling en retry-strategieën

Robuuste faalafhandeling onderscheidt productiewaardige queue-implementaties van naïeve oplossingen. Laravel biedt meerdere mechanismen om het retry-gedrag te configureren, waaronder exponentieel toenemende backoff-intervallen, maximale uitzonderingen en tijdsgebonden retry-limieten.

App/Jobs/SyncExternalData.phpphp
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));
    }
}

Het verschil tussen $this->release() en een thrown exception is significant. Bij release() wordt de job opnieuw in de queue geplaatst zonder dat de $tries-teller wordt verhoogd. Een exception verhoogt de teller wel. De $maxExceptions-property beperkt het aantal daadwerkelijke fouten, terwijl retryUntil() een absolute tijdslimiet instelt.

Queue Worker Management en deployment

In productieomgevingen worden queue workers beheerd door een process manager zoals Supervisor. Deze zorgt ervoor dat workers automatisch herstarten bij een crash en dat meerdere worker-processen parallel draaien.

ini
; /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

De --max-time=3600 flag zorgt ervoor dat elke worker na maximaal een uur gracefully herstart, wat geheugenlekken voorkomt. De numprocs=4 instelling start vier parallelle worker-processen. Bij deployments moet php artisan queue:restart worden uitgevoerd om workers de laatste codewijzigingen te laten oppikken, aangezien workers lang levende processen zijn die de applicatiecode bij opstart in het geheugen laden.

Unieke jobs en versleutelde payloads

Voor jobs die niet dubbel uitgevoerd mogen worden -- zoals het herbouwen van een zoekindex -- biedt Laravel de ShouldBeUnique interface. Deze plaatst een atomaire lock die voorkomt dat dezelfde job meerdere keren tegelijk in de queue staat.

App/Jobs/RebuildSearchIndex.phpphp
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);
    }
}

Wanneer jobs gevoelige gegevens bevatten, zoals betaaltokens, kan de ShouldBeEncrypted interface worden gebruikt. Hiermee wordt de volledige job-payload versleuteld voordat deze op de queue wordt geplaatst:

App/Jobs/ProcessPayment.phpphp
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);
    }
}

De encryptie maakt gebruik van Laravel's APP_KEY en zorgt ervoor dat gevoelige data niet leesbaar is in de queue-backend, zelfs niet voor beheerders met directe toegang tot Redis of de database.

Veelgestelde interviewvragen over Laravel Queues

Technische interviews voor Laravel-posities bevatten regelmatig vragen over het queue-systeem. Hieronder een overzicht van de meest voorkomende onderwerpen en wat een sterk antwoord kenmerkt.

Wat is het verschil tussen dispatch() en dispatchSync()? De methode dispatch() plaatst de job op de queue voor asynchrone verwerking, terwijl dispatchSync() de job direct binnen het huidige proces uitvoert. De synchrone variant is nuttig voor testing en situaties waarin het resultaat onmiddellijk nodig is.

Hoe werkt SerializesModels en waarom is het belangrijk? Deze trait serialiseert Eloquent-modellen door alleen hun identifier (primary key) en eventuele geladen relaties op te slaan. Bij deserialisatie wordt het model opnieuw uit de database opgehaald. Dit voorkomt verouderde data en houdt de payload compact.

Wat is het verschil tussen job batching en job chaining? Batching voert jobs parallel uit en biedt callbacks voor de collectieve uitkomst. Chaining voert jobs sequentieel uit, waarbij elke job pas start na succesvolle afronding van de vorige. Batching is geschikt voor onafhankelijke taken; chaining voor workflows met afhankelijkheden.

Hoe voorkom je race conditions bij queue jobs? Door gebruik te maken van WithoutOverlapping-middleware, ShouldBeUnique voor unieke jobs, en atomaire locks via Cache::lock(). Deze mechanismen garanderen dat conflicterende jobs niet gelijktijdig worden uitgevoerd.

Wat is exponential backoff en wanneer pas je het toe? Exponential backoff verhoogt de wachttijd tussen retry-pogingen progressief. Dit vermindert de belasting op falende externe services en vergroot de kans op succesvol herstel. Het wordt geconfigureerd via de backoff()-methode die een array van seconden retourneert.

Hoe monitor je queue-health in productie? Laravel Horizon biedt een dashboard voor Redis-queues met real-time metrics over doorvoer, wachttijden en faalpercentages. Daarnaast kunnen queue:monitor en custom health checks geïntegreerd worden met monitoring-tools zoals Datadog of New Relic.

Conclusie

Het queue-systeem van Laravel vormt een essentieel onderdeel van schaalbare applicatiearchitectuur. De belangrijkste concepten voor zowel dagelijkse ontwikkeling als technische interviews zijn:

  • Job dispatching via de ShouldQueue interface ontkoppelt zware taken van het HTTP-verzoek
  • Job batching maakt parallelle verwerking mogelijk met voortgangsbewaking en faalafhandeling
  • Job chaining garandeert sequentiële uitvoering voor workflows met afhankelijkheden
  • Queue middleware biedt herbruikbare patronen voor rate limiting en overlap-preventie
  • Retry-strategieën met exponential backoff en tijdslimieten verhogen de betrouwbaarheid
  • Unieke jobs en versleutelde payloads bieden bescherming tegen duplicatie en datalekken
  • Supervisor en Horizon zijn onmisbaar voor productie-deployments en monitoring

Een gedegen begrip van deze concepten stelt ontwikkelaars in staat om niet alleen betrouwbare asynchrone systemen te bouwen, maar ook om technische gesprekken over queue-architectuur met vertrouwen te voeren.

Voor praktische voorbereiding op Laravel-interviewvragen biedt de SharpSkill-vragenbank queues, middleware en Eloquent-patronen met gedetailleerde uitleg.

Begin met oefenen!

Test je kennis met onze gespreksimulatoren en technische tests.

Tags

#laravel
#queues
#jobs
#php
#async
#architecture

Delen

Gerelateerde artikelen