Cosa sono le code
Nelle applicazioni web puoi avere operazioni che richiedono qualche secondo: invio email, ridimensionamento immagini, chiamate ad API esterne.
Eseguirle sincronamente in una richiesta HTTP costringe l’utente ad aspettare.
Con le code di Laravel esegui questi lavori in background in modo asincrono .
La richiesta risponde subito e i worker si occupano del lavoro.
Le code supportano più backend: database, Redis, Amazon SQS, ecc.
In sviluppo, con il driver sync i job vengono eseguiti immediatamente senza coda.
Configurazione delle code
config/queue.php
Impostazioni in config/queue.php. Cambia driver con QUEUE_CONNECTION.
'default' => env ( 'QUEUE_CONNECTION' , 'database' ),
.env
QUEUE_CONNECTION =database
# Redis
# QUEUE_CONNECTION=redis
# REDIS_HOST=127.0.0.1
# REDIS_PORT=6379
Preparazione del driver database
Serve una tabella jobs. In Laravel 11+ è già inclusa nella migration.
Altrimenti:
php artisan make:queue-table
php artisan migrate
Preparazione del driver Redis
Aggiungi la connessione in config/database.php e installa il driver.
composer require predis/predis
SQS Overflow Storage
Amazon SQS ha un limite sulla dimensione dei payload. Per job con payload grandi puoi salvare l’eccesso in un cache store e inviare solo un puntatore.
'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 attivo: payload > 1MB salvati nel cache store.
always a true: salva sempre nel cache store.
delete_after_processing (default true): elimina i payload salvati al successo.
flush_on_clear a true: queue:clear esegue flush dello store di overflow. Usa uno store dedicato.
Creazione della classe job
make:job
php artisan make:job SendWelcomeEmail
Genera app/Jobs/SendWelcomeEmail.php.
Struttura del job
<? 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 ;
public function __construct (
public User $user ,
) {}
public function handle () : void
{
Mail :: to ( $this -> user -> email ) -> send ( new WelcomeMail ( $this -> user ));
}
}
ShouldQueue indica che va elaborato via coda. Il trait Queueable fornisce i metodi necessari.
Passando un modello Eloquent al costruttore, Laravel serializza solo l’ID e ricarica i dati aggiornati in fase di esecuzione: payload più leggeri.
Dispatch del job
dispatch()
use App\Jobs\ SendWelcomeEmail ;
public function register ( Request $request ) : RedirectResponse
{
$user = User :: create ( $request -> validated ());
SendWelcomeEmail :: dispatch ( $user );
return redirect ( '/dashboard' );
}
Ritardato
SendWelcomeEmail :: dispatch ( $user ) -> delay ( now () -> addMinutes ( 5 ));
dispatchAfterResponse()
Esegui subito dopo la risposta HTTP. Funziona anche con sync.
SendWelcomeEmail :: dispatchAfterResponse ( $user );
Coda specifica
SendWelcomeEmail :: dispatch ( $user ) -> onQueue ( 'emails' );
Queue Routing
Instrada per default certi job a una connessione/coda tramite Queue::route() nel boot() di un ServiceProvider.
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' );
}
Puoi indicare interfacce, trait o classi genitore.
Più job insieme:
Queue :: route ([
ProcessPodcast :: class => [ 'podcasts' , 'redis' ],
ProcessVideo :: class => 'videos' ,
]);
Il Queue Routing è sovrascrivibile a livello di job con onQueue() / onConnection().
Esecuzione sincrona (test/sviluppo)
SendWelcomeEmail :: dispatchSync ( $user );
Dispatch in bulk
Per molti job indipendenti senza tracciamento come nei batch:
use App\Jobs\ ProcessUser ;
use Illuminate\Support\Facades\ Bus ;
Bus :: bulk (
$users -> map ( fn ( $user ) => new ProcessUser ( $user ))
);
Bus::bulk() invia in modo efficiente ma senza le funzionalità di batch (progresso, callback).
Elaborazione dei job
queue:work
Con driver/coda specifici:
php artisan queue:work redis --queue=emails
php artisan queue:work database
queue:work resta in esecuzione. Riavvia con queue:restart dopo modifiche.
In produzione usa Supervisor.
Opzioni del worker
php artisan queue:work --tries=3 --timeout=60 --sleep=3
Opzione Descrizione --tries=NTentativi max prima del fallimento --timeout=NSecondi max per job --sleep=NAttesa quando la coda è vuota (default 3) --max-jobs=NTermina dopo N job --max-time=NTermina dopo N secondi --queue=A,BPriorità di code (A prima)
Impostazioni nella classe
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 ;
}
Rilascio del job (middleware Release)
Per rimettere il job in coda al soddisfacimento di una condizione.
use Illuminate\Queue\Middleware\ Release ;
public function middleware () : array
{
return [
Release :: when ( $this -> order -> isPending (), releaseAfter : 60 ),
];
}
Release::unless() rilascia quando la condizione è false.
return [
Release :: unless ( $this -> order -> isPaid (), releaseAfter : 60 ),
];
Con closure:
return [
Release :: when ( function () : bool {
return ! $this -> order -> isPaid ();
}, releaseAfter : 60 ),
];
Rilasciare incrementa il contatore dei tentativi. Configura correttamente #[Tries] o $tries.
Gestione dei job falliti
Tabella failed_jobs
php artisan make:queue-failed-table
php artisan migrate
Cleanup al fallimento
use Throwable ;
public function failed ( ? Throwable $exception ) : void
{
// Notifica ad admin, ecc.
}
Interrompere il retry per certe eccezioni
In withExceptions() di bootstrap/app.php:
use App\Exceptions\ InvalidPodcastSourceException ;
use Illuminate\Foundation\Configuration\ Exceptions ;
-> withExceptions ( function ( Exceptions $exceptions ) : void {
$exceptions -> dontRetry ([
InvalidPodcastSourceException :: class ,
]);
})
Con closure:
use App\Exceptions\ PodcastProcessingException ;
use Illuminate\Foundation\Configuration\ Exceptions ;
-> withExceptions ( function ( Exceptions $exceptions ) : void {
$exceptions -> dontRetryWhen ( function ( PodcastProcessingException $e ) {
return $e -> reason () === 'Subscription expired' ;
});
})
Per eccezioni deterministiche (validazione, sottoscrizione scaduta) è efficiente fallire subito.
Elenco job falliti
Retry
php artisan queue:retry ce7bb17c-cdd8-41f0-a8ec-7b4fef4e5ece
php artisan queue:retry all
Eliminazione
php artisan queue:forget ce7bb17c-cdd8-41f0-a8ec-7b4fef4e5ece
php artisan queue:flush
Driver più usati
database
Semplice, senza middleware aggiuntivi. Salva su jobs e i worker fanno polling.
Pro: setup semplice, riusa RDBMS
Contro: carico DB, non adatto a grandi volumi
QUEUE_CONNECTION =database
redis
Il più usato in produzione. In memoria, alta throughput.
Pro: veloce, scalabile
Contro: richiede server Redis
QUEUE_CONNECTION =redis
REDIS_HOST =127.0.0.1
REDIS_PORT =6379
Esercizio in produzione con Supervisor
In produzione serve un supervisor che riavvii queue:work se cade.
[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 per due worker in parallelo.
sudo supervisorctl reread
sudo supervisorctl update
sudo supervisorctl start laravel-worker: *
Esempio pratico: invio email in coda
Crea il job
php artisan make:job SendOrderConfirmation
Implementa
<? 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 ));
}
}
Dispatch dal controller
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' , 'Ordine ricevuto.' );
}
Avvia il worker
php artisan queue:work --tries=3 --timeout=30
Riepilogo
Invio email/SMS
Elaborazione immagini/video
Chiamate ad API esterne
Generazione report o export CSV
Invio webhook
Con QUEUE_CONNECTION=sync i job vengono eseguiti subito senza worker.
php artisan queue:work
php artisan queue:restart
php artisan queue:failed
php artisan queue:retry all
php artisan queue:flush