Passer au contenu principal

Qu’est-ce qu’une queue

Dans une application web, l’envoi de mails, le redimensionnement d’images ou les appels d’API externes peuvent prendre plusieurs secondes. Traiter cela de manière synchrone dans une requête HTTP fait attendre l’utilisateur inutilement. Les queues Laravel exécutent ces tâches en arrière-plan : la requête retourne immédiatement une réponse, et un worker traite le job à part.
Backends supportés : base de données, Redis, Amazon SQS… En dev, le driver sync exécute les jobs immédiatement, sans queue.

Configuration

config/queue.php

// config/queue.php
'default' => env('QUEUE_CONNECTION', 'database'),

.env

# Sélection du driver
QUEUE_CONNECTION=database

# Redis
# QUEUE_CONNECTION=redis
# REDIS_HOST=127.0.0.1
# REDIS_PORT=6379

Préparer le driver database

database nécessite une table jobs. Sous Laravel 11+, la migration est incluse ; sinon :
php artisan make:queue-table
php artisan migrate

Préparer le driver Redis

Configurez la connexion Redis dans config/database.php puis installez :
composer require predis/predis

Overflow storage SQS

Amazon SQS limite la taille de payload. Pour de gros payloads, redirigez l’excédent vers un cache et n’envoyez qu’un pointeur.
'sqs' => [
    // ...
    'overflow' => [
        'enabled' => env('SQS_OVERFLOW_ENABLED', false),
        'store' => env('SQS_OVERFLOW_STORE'),
        'always' => false,
        'delete_after_processing' => true,
        'flush_on_clear' => env('SQS_OVERFLOW_FLUSH_ON_CLEAR', false),
    ],
],
  • enabled : payloads ≥ 1 Mo → cache.
  • always: true : toujours passer par le cache.
  • delete_after_processing : nettoie le cache après succès (true par défaut).
  • flush_on_clear: true : queue:clear purge le cache d’overflow. À combiner avec un store dédié pour ne pas vider le cache général.

Créer une classe de job

Commande make:job

php artisan make:job SendWelcomeEmail
Le fichier app/Jobs/SendWelcomeEmail.php est créé.

Structure

<?php

namespace App\Jobs;

use App\Models\User;
use App\Mail\WelcomeMail;
use Illuminate\Contracts\Queue\ShouldQueue;
use Illuminate\Foundation\Queue\Queueable;
use Illuminate\Support\Facades\Mail;

class SendWelcomeEmail implements ShouldQueue
{
    use Queueable;

    /**
     * Instance du job.
     */
    public function __construct(
        public User $user,
    ) {}

    /**
     * Exécution du job.
     */
    public function handle(): void
    {
        Mail::to($this->user->email)->send(new WelcomeMail($this->user));
    }
}
ShouldQueue indique un traitement asynchrone. Le trait Queueable fournit les méthodes nécessaires.
Passer un modèle Eloquent au constructeur sérialise seulement son ID. Au moment de l’exécution, le modèle est récupéré à jour : payload allégé.

Dispatch

dispatch()

use App\Jobs\SendWelcomeEmail;

public function register(Request $request): RedirectResponse
{
    $user = User::create($request->validated());

    SendWelcomeEmail::dispatch($user);

    return redirect('/dashboard');
}

Dispatch différé

// 5 min plus tard
SendWelcomeEmail::dispatch($user)->delay(now()->addMinutes(5));

dispatchAfterResponse()

Exécute le job après l’envoi de la réponse HTTP. Fonctionne aussi en sync — idéal pour du léger sans worker.
SendWelcomeEmail::dispatchAfterResponse($user);

Vers une queue précise

SendWelcomeEmail::dispatch($user)->onQueue('emails');

Routing de queue

Pour rediriger par défaut certaines classes vers une connexion/queue précise, utilisez Queue::route() dans boot().
use App\Concerns\RequiresVideo;
use App\Jobs\ProcessPodcast;
use App\Jobs\ProcessVideo;
use Illuminate\Support\Facades\Queue;

public function boot(): void
{
    Queue::route(ProcessPodcast::class, connection: 'redis', queue: 'podcasts');
    Queue::route(RequiresVideo::class, queue: 'video');
}
Ciblez interface, trait, classe parente… tout ce qui les implémente/étend est routé. Plusieurs routes en une :
Queue::route([
    ProcessPodcast::class => ['podcasts', 'redis'], // queue et connexion
    ProcessVideo::class => 'videos',                // queue seule (connexion par défaut)
]);
Un onQueue() / onConnection() explicite sur le job prime sur le routing.

Sync (dev/tests)

dispatchSync() exécute immédiatement, sans queue.
SendWelcomeEmail::dispatchSync($user);

Bulk dispatch

Pour envoyer beaucoup de jobs indépendants d’un coup sans tracking : Bus::bulk().
use App\Jobs\ProcessUser;
use Illuminate\Support\Facades\Bus;

Bus::bulk(
    $users->map(fn ($user) => new ProcessUser($user))
);
Bus::bulk() groupe les jobs par connexion/queue et les envoie par lot. Contrairement à Bus::batch(), pas de suivi ni de callback.

Traiter les jobs

queue:work

php artisan queue:work
Ciblez driver et file :
# File emails sur Redis
php artisan queue:work redis --queue=emails

# Driver database
php artisan queue:work database
queue:work reste actif. Après un changement de code, queue:restart recharge les workers. En production, un superviseur (Supervisor…) est recommandé.

Options de supervision

php artisan queue:work --tries=3 --timeout=60 --sleep=3
OptionDescription
--tries=NTentatives max ; au-delà, échec
--timeout=NDurée max d’un job (secondes)
--sleep=NAttente entre polls quand la file est vide (3 s)
--max-jobs=NTermine après N jobs
--max-time=NTermine après N secondes
--queue=A,BPriorise A avant B

Retry paramétré côté job

Souvent plus lisible que la CLI.
use Illuminate\Foundation\Queue\Queueable;
use Illuminate\Queue\Attributes\Tries;
use Illuminate\Queue\Attributes\Timeout;

#[Tries(3)]
#[Timeout(60)]
class SendWelcomeEmail implements ShouldQueue
{
    use Queueable;

    // ...
}

Libération de job (middleware Release)

Pour renvoyer le job dans la queue sans l’exécuter, sous condition, utilisez le middleware Release.
use Illuminate\Queue\Middleware\Release;

/**
 * Middlewares que le job traverse.
 */
public function middleware(): array
{
    return [
        // Si $condition == true, libère après 60 s
        Release::when($this->order->isPending(), releaseAfter: 60),
    ];
}
Release::unless() libère si la condition est false.
return [
    Release::unless($this->order->isPaid(), releaseAfter: 60),
];
Une closure pour des cas plus complexes :
return [
    Release::when(function (): bool {
        return ! $this->order->isPaid();
    }, releaseAfter: 60),
];
Chaque libération incrémente le compteur d’essais. Réglez #[Tries] ou $tries en conséquence.

Jobs échoués

Table failed_jobs

Après épuisement des tentatives, le job est stocké dans failed_jobs.
php artisan make:queue-failed-table
php artisan migrate

Cleanup à l’échec

Définissez failed() sur le job.
use Throwable;

public function failed(?Throwable $exception): void
{
    // Notifier un admin Slack, etc.
    // Notification::route('slack', config('app.slack_webhook'))
    //     ->notify(new JobFailedNotification($this, $exception));
}

Arrêter les retries par type d’exception

Utilisez dontRetry dans withExceptions() (fichier bootstrap/app.php).
use App\Exceptions\InvalidPodcastSourceException;
use Illuminate\Foundation\Configuration\Exceptions;

->withExceptions(function (Exceptions $exceptions): void {
    $exceptions->dontRetry([
        InvalidPodcastSourceException::class,
    ]);
})
Avec une closure pour un contrôle fin :
use App\Exceptions\PodcastProcessingException;
use Illuminate\Foundation\Configuration\Exceptions;

->withExceptions(function (Exceptions $exceptions): void {
    $exceptions->dontRetryWhen(function (PodcastProcessingException $e) {
        return $e->reason() === 'Subscription expired';
    });
})
Idéal pour des cas où retenter ne changera rien (validation, échec de paiement, abonnement expiré…).

Lister les échecs

php artisan queue:failed

Retry

# Un job précis
php artisan queue:retry ce7bb17c-cdd8-41f0-a8ec-7b4fef4e5ece

# Tous
php artisan queue:retry all

Suppression

# Un job
php artisan queue:forget ce7bb17c-cdd8-41f0-a8ec-7b4fef4e5ece

# Tous
php artisan queue:flush

Drivers courants

database

Simple à mettre en place, utilise la RDBMS existante.
  • Avantages : setup rapide, sans dépendance
  • Inconvénients : lourd pour de gros volumes
QUEUE_CONNECTION=database

redis

Driver rapide, très utilisé en production. Débit supérieur.
  • Avantages : rapide, scalable
  • Inconvénients : nécessite Redis
QUEUE_CONNECTION=redis
REDIS_HOST=127.0.0.1
REDIS_PORT=6379
En production, Laravel Horizon offre un beau dashboard pour surveiller les jobs Redis en temps réel.

Supervisor en production

Sous Linux, faites tourner queue:work en continu avec Supervisor.
# /etc/supervisor/conf.d/laravel-worker.conf
[program:laravel-worker]
process_name=%(program_name)s_%(process_num)02d
command=php /var/www/your-app/artisan queue:work --sleep=3 --tries=3 --max-time=3600
autostart=true
autorestart=true
stopasgroup=true
killasgroup=true
user=www-data
numprocs=2
redirect_stderr=true
stdout_logfile=/var/www/your-app/storage/logs/worker.log
stopwaitsecs=3600
numprocs=2 démarre 2 workers en parallèle. Rechargez Supervisor :
sudo supervisorctl reread
sudo supervisorctl update
sudo supervisorctl start laravel-worker:*

Exemple : envoi d’e-mail en queue

1

Créer le job

php artisan make:job SendOrderConfirmation
2

Implémenter

<?php

namespace App\Jobs;

use App\Models\Order;
use App\Mail\OrderConfirmed;
use Illuminate\Contracts\Queue\ShouldQueue;
use Illuminate\Foundation\Queue\Queueable;
use Illuminate\Queue\Attributes\Tries;
use Illuminate\Queue\Attributes\Timeout;
use Illuminate\Support\Facades\Mail;

#[Tries(3)]
#[Timeout(30)]
class SendOrderConfirmation implements ShouldQueue
{
    use Queueable;

    public function __construct(
        public Order $order,
    ) {}

    public function handle(): void
    {
        Mail::to($this->order->user->email)
            ->send(new OrderConfirmed($this->order));
    }
}
3

Dispatcher depuis un contrôleur

use App\Jobs\SendOrderConfirmation;

public function store(Request $request): RedirectResponse
{
    $order = Order::create($request->validated());

    SendOrderConfirmation::dispatch($order);

    return redirect()->route('orders.show', $order)
        ->with('success', 'Commande enregistrée.');
}
4

Démarrer un worker

php artisan queue:work --tries=3 --timeout=30

Récapitulatif

  • Envoi de mails / SMS
  • Redimensionnement d’images / vidéos
  • Appels d’API externes
  • Génération de rapports / exports CSV
  • Envoi de webhooks
QUEUE_CONNECTION=sync dans .env exécute immédiatement — pas besoin de worker pour tester.
QUEUE_CONNECTION=sync
php artisan queue:work

php artisan queue:restart

php artisan queue:failed

php artisan queue:retry all

php artisan queue:flush
Dernière modification le 13 juillet 2026