Cos’è il task scheduling
Tradizionalmente devi scrivere una voce cron per ciascun task ricorrente sul server.
La definizione vive fuori dal codice sorgente, non è versionata e ogni modifica richiede un SSH.
Con lo scheduler di Laravel definisci lo schedule fluentemente nell’applicazione.
Sul server basta una sola voce cron e lo schedule è versionato con il codice.
Il posto standard è routes/console.php.
<?php
use Illuminate\Support\Facades\DB;
use Illuminate\Support\Facades\Schedule;
Schedule::call(function () {
DB::table('recent_users')->delete();
})->daily();
Con schedule:list vedi task e prossime esecuzioni.php artisan schedule:list
Flusso dello scheduler
Definizione dello schedule
Definisci in routes/console.php. In alternativa in withSchedule di bootstrap/app.php.
use Illuminate\Console\Scheduling\Schedule;
->withSchedule(function (Schedule $schedule) {
$schedule->call(new DeleteRecentUsers)->daily();
})
Tipi di task pianificabili
Comandi Artisan
Con command.
use App\Console\Commands\SendEmailsCommand;
use Illuminate\Support\Facades\Schedule;
Schedule::command('emails:send Taylor --force')->daily();
Schedule::command(SendEmailsCommand::class, ['Taylor', '--force'])->daily();
I comandi definiti tramite closure possono concatenare direttamente lo scheduling.
Artisan::command('delete:recent-users', function () {
DB::table('recent_users')->delete();
})->purpose('Delete recent users')->daily();
Job in coda
use App\Jobs\Heartbeat;
use Illuminate\Support\Facades\Schedule;
Schedule::job(new Heartbeat)->everyFiveMinutes();
Coda e connessione:
Schedule::job(new Heartbeat, 'heartbeats', 'sqs')->everyFiveMinutes();
Comandi shell
Schedule::exec('node /home/forge/script.js')->daily();
Closure
Schedule::call(function () {
DB::table('recent_users')->delete();
})->daily();
Oggetti invocabili:
Schedule::call(new DeleteRecentUsers)->daily();
Frequenza
| Metodo | Descrizione |
|---|
->everySecond() | Ogni secondo |
->everyMinute() | Ogni minuto |
->everyFiveMinutes() | Ogni 5 minuti |
->everyFifteenMinutes() | Ogni 15 minuti |
->everyThirtyMinutes() | Ogni 30 minuti |
->hourly() | Ogni ora |
->hourlyAt(17) | Ogni ora al minuto 17 |
->daily() | Ogni giorno a mezzanotte |
->dailyAt('13:00') | Ogni giorno alle 13 |
->twiceDaily(1, 13) | Ogni giorno alle 1 e alle 13 |
->weekly() | Ogni domenica mezzanotte |
->weeklyOn(1, '8:00') | Ogni lunedì alle 8 |
->monthly() | 1° del mese mezzanotte |
->monthlyOn(4, '15:00') | Il 4 di ogni mese alle 15 |
->quarterly() | Inizio trimestre mezzanotte |
->yearly() | 1° gennaio mezzanotte |
->timezone('Europe/Rome') | Timezone |
Espressione cron
Schedule::command('emails:send')->cron('0 9 * * *');
Combinazione frequenza + giorno
Schedule::call(function () {
// ...
})->weekly()->mondays()->at('13:00');
Schedule::command('foo')
->weekdays()
->hourly()
->timezone('Europe/Rome')
->between('8:00', '17:00');
Timezone
Schedule::command('report:generate')
->timezone('Europe/Rome')
->at('9:00');
Globale in config/app.php:
'schedule_timezone' => 'Europe/Rome',
Con l’ora legale i task potrebbero eseguirsi due volte o non eseguirsi affatto. Consigliato UTC quando possibile.
Vincoli condizionali
when / skip
Schedule::command('emails:send')->daily()->when(function () {
return true;
});
Schedule::command('emails:send')->daily()->skip(function () {
return true;
});
environments
Schedule::command('emails:send')
->daily()
->environments(['staging', 'production']);
Orario
Schedule::command('emails:send')
->hourly()
->between('7:00', '22:00');
Schedule::command('emails:send')
->hourly()
->unlessBetween('23:00', '4:00');
Giorno
| Metodo | Descrizione |
|---|
->weekdays() | Feriali |
->weekends() | Weekend |
->mondays() … ->sundays() | Giorno specifico |
->days([0, 3]) | Più giorni (0 = domenica, 3 = mercoledì) |
use Illuminate\Support\Facades;
use Illuminate\Console\Scheduling\Schedule;
Facades\Schedule::command('emails:send')
->hourly()
->days([Schedule::SUNDAY, Schedule::WEDNESDAY]);
Prevenzione sovrapposizioni
Con withoutOverlapping la prossima esecuzione attende la fine della precedente.
Schedule::command('emails:send')->withoutOverlapping();
Durata del lock in minuti (default 24h):
Schedule::command('emails:send')->withoutOverlapping(10);
Il lock usa la cache. Per svuotarlo:php artisan schedule:clear-cache
Esecuzione su più server
Con onOneServer solo una macchina esegue il task.
Schedule::command('report:generate')
->fridays()
->at('17:00')
->onOneServer();
Richiede driver cache database, memcached, dynamodb o redis condiviso.
Flusso di controllo distribuito
Raggruppare i task
Schedule::daily()
->onOneServer()
->timezone('Europe/Rome')
->group(function () {
Schedule::command('emails:send --force');
Schedule::command('emails:prune');
});
Esecuzione in background
I task pianificati alla stessa ora vengono eseguiti in ordine.
Con runInBackground li esegui in parallelo.
Schedule::command('analytics:report')
->daily()
->runInBackground();
Solo per command ed exec.
Modalità manutenzione
In manutenzione i task pianificati non partono. Per forzarli:
Schedule::command('emails:send')->evenInMaintenanceMode();
Pausa dello scheduler
php artisan schedule:pause
php artisan schedule:continue
Con evenWhenPaused alcuni task continuano.
Schedule::command('emails:send')->evenWhenPaused();
Output
Su file
Schedule::command('emails:send')
->daily()
->sendOutputTo(storage_path('logs/emails-send.log'));
appendOutputTo accoda.
Schedule::command('emails:send')
->daily()
->appendOutputTo(storage_path('logs/emails-send.log'));
Via email
Schedule::command('report:generate')
->daily()
->sendOutputTo($filePath)
->emailOutputTo('[email protected]');
Solo fallimento:
Schedule::command('report:generate')
->daily()
->emailOutputOnFailure('[email protected]');
Hook
Schedule::command('emails:send')
->daily()
->before(function () {
// Prima
})
->after(function () {
// Dopo
});
Successo/fallimento:
use Illuminate\Support\Stringable;
Schedule::command('emails:send')
->daily()
->onSuccess(function (Stringable $output) {
// Successo
})
->onFailure(function (Stringable $output) {
// Fallimento
});
Deploy sul server
Aggiungi una voce cron
* * * * * cd /path-to-your-project && php artisan schedule:run >> /dev/null 2>&1
Modifica con crontab -e.Verifica
php artisan schedule:list
In sviluppo locale
php artisan schedule:work
Foreground, chiama lo scheduler ogni minuto finché non premi Ctrl+C.
Sub-minuto
Schedule::call(function () {
DB::table('recent_users')->delete();
})->everySecond();
schedule:run resta attivo per completare tutti i task sub-minuto.
Delega ai job in coda per non bloccare l’esecuzione.
Per interrompere in deploy:
php artisan schedule:interrupt
Comandi comuni
php artisan schedule:list
php artisan schedule:run
php artisan schedule:work
php artisan schedule:pause
php artisan schedule:continue
php artisan schedule:clear-cache
php artisan schedule:interrupt