Task scheduling com Laravel Scheduler
1. Introdução ao Agendamento de Tarefas no Laravel
1.1. O problema do cron tradicional vs. Laravel Scheduler
Gerenciar tarefas agendadas em servidores web sempre foi um desafio. No modelo tradicional, cada tarefa exige uma entrada separada no crontab do servidor, o que rapidamente se torna um pesadelo de manutenção. Você precisa acessar o servidor, editar arquivos de configuração e lidar com sintaxes diferentes entre distribuições Linux.
O Laravel Scheduler resolve esse problema de forma elegante. Com ele, você define todas as suas tarefas agendadas em um único arquivo PHP, usando a sintaxe familiar do framework. Uma única entrada no cron do servidor é suficiente para gerenciar dezenas ou centenas de tarefas.
1.2. Como o Scheduler funciona por baixo dos panos
O coração do Scheduler é o comando php artisan schedule:run. Quando o cron do servidor executa esse comando a cada minuto, o Laravel verifica todas as tarefas registradas e executa aquelas cuja frequência coincide com o horário atual. Isso significa que a precisão máxima é de um minuto — suficiente para a grande maioria dos casos de uso.
1.3. Pré-requisitos: configurar o cron no servidor
Para começar, adicione esta única linha ao crontab do seu servidor:
* * * * * cd /caminho/para/seu/projeto && php artisan schedule:run >> /dev/null 2>&1
Esse comando executa o agendador a cada minuto. O redirecionamento para /dev/null evita que logs desnecessários poluam o terminal.
2. Definindo Tarefas Agendadas: Sintaxe e Frequências
2.1. Estrutura do método schedule() no App\Console\Kernel
Toda a configuração das tarefas acontece no arquivo app/Console/Kernel.php, dentro do método schedule():
<?php
namespace App\Console;
use Illuminate\Console\Scheduling\Schedule;
use Illuminate\Foundation\Console\Kernel as ConsoleKernel;
class Kernel extends ConsoleKernel
{
protected function schedule(Schedule $schedule)
{
// Suas tarefas aqui
}
}
2.2. Frequências comuns
O Laravel oferece uma API fluente para definir frequências:
$schedule->command('emails:send')->daily(); // Executa uma vez por dia à meia-noite
$schedule->command('reports:generate')->hourly(); // Executa no início de cada hora
$schedule->command('cache:clear')->everyMinute(); // Executa a cada minuto
$schedule->command('backup:run')->cron('0 2 * * 0'); // Domingo às 2h da manhã
2.3. Executando comandos Artisan, closures e scripts externos
O Scheduler aceita três tipos de tarefas:
// Comando Artisan
$schedule->command('inspire')->daily();
// Closure (cuidado: não tem acesso ao container completo)
$schedule->call(function () {
\Log::info('Tarefa executada manualmente');
})->everyMinute();
// Script externo
$schedule->exec('/usr/bin/python3 /scripts/cleanup.py')->daily();
3. Condições e Restrições de Execução
3.1. Métodos condicionais
Use when() para executar apenas se uma condição for verdadeira, e skip() para pular quando necessário:
$schedule->command('reports:generate')
->daily()
->when(function () {
return \App\Models\Order::whereDate('created_at', today())->count() > 0;
});
$schedule->command('emails:reminder')
->hourly()
->skip(function () {
return today()->isWeekend();
});
3.2. Restrições de ambiente
Controle em quais ambientes a tarefa deve rodar:
$schedule->command('backup:database')
->daily()
->environments('production');
$schedule->command('maintenance:check')
->hourly()
->between('8:00', '18:00')
->unlessBetween('12:00', '13:00');
3.3. Evitando sobreposição
Para tarefas que podem demorar mais que o intervalo entre execuções:
$schedule->command('backup:full')
->daily()
->withoutOverlapping(60); // Timeout de 60 minutos
4. Saída, Logs e Notificações de Tarefas
4.1. Redirecionando saída
$schedule->command('reports:export')
->daily()
->sendOutputTo(storage_path('logs/reports.log'))
->appendOutputTo(storage_path('logs/reports_history.log'));
4.2. Enviando resultados por e-mail
$schedule->command('orders:summary')
->daily()
->emailOutputTo('admin@exemplo.com');
4.3. Notificações de falha
$schedule->command('health:check')
->everyMinute()
->pingBefore('https://health.example.com/start')
->thenPing('https://health.example.com/success');
5. Tarefas em Background e Execução Assíncrona
5.1. Executando tarefas em fila
$schedule->job(new \App\Jobs\ProcessOrders)
->hourly()
->onQueue('high')
->onConnection('redis');
5.2. Comandos longos e execução em background
$schedule->command('reports:heavy')
->daily()
->runInBackground(); // Libera o scheduler imediatamente
5.3. Diferenças entre execução síncrona e assíncrona
Na execução síncrona (padrão), o scheduler espera a tarefa terminar antes de processar a próxima. Com runInBackground(), a tarefa é enviada para execução em segundo plano, permitindo que outras tarefas sejam processadas simultaneamente.
6. Manutenção, Monitoramento e Boas Práticas
6.1. Verificando tarefas agendadas
php artisan schedule:list
Esse comando mostra todas as tarefas registradas, suas frequências e próximas execuções.
6.2. Testando tarefas localmente
php artisan schedule:work
Esse comando executa o scheduler continuamente no terminal, ideal para testes em desenvolvimento.
6.3. Boas práticas
- Idempotência: Garanta que executar a mesma tarefa múltiplas vezes não cause efeitos colaterais
- Tratamento de erros: Use try-catch nas closures e comandos
- Logs estruturados: Inclua identificadores únicos nas tarefas para facilitar debugging
$schedule->command('cleanup:temp')
->daily()
->onSuccess(function () {
\Log::info('Limpeza concluída com sucesso');
})
->onFailure(function () {
\Log::error('Falha na limpeza');
});
7. Casos de Uso Avançados e Integrações
7.1. Agendamento dinâmico com banco de dados
protected function schedule(Schedule $schedule)
{
$tasks = \App\Models\ScheduledTask::where('active', true)->get();
foreach ($tasks as $task) {
$schedule->command($task->command)
->cron($task->cron_expression)
->withoutOverlapping();
}
}
7.2. Integração com filas Redis e Horizon
$schedule->command('process:notifications')
->everyMinute()
->withoutOverlapping()
->onConnection('redis')
->onQueue('notifications');
7.3. Exemplo completo
protected function schedule(Schedule $schedule)
{
// Backup automático do banco
$schedule->command('backup:database')
->dailyAt('02:00')
->environments('production')
->withoutOverlapping()
->sendOutputTo(storage_path('logs/backup.log'));
// Limpeza de cache
$schedule->command('cache:clear')
->daily()
->appendOutputTo(storage_path('logs/cache_clear.log'));
// Envio de relatórios semanais
$schedule->command('reports:weekly')
->weekly()
->mondays()
->at('09:00')
->emailOutputTo('equipe@exemplo.com');
// Verificação de saúde do sistema
$schedule->command('health:check')
->everyFiveMinutes()
->thenPing('https://monitor.exemplo.com/healthy');
}
Referências
- Laravel Documentation: Scheduling — Documentação oficial completa sobre o Laravel Scheduler, incluindo todas as opções de frequência, condições e integrações.
- Laravel Daily: Laravel Task Scheduling Tutorial — Tutorial prático com exemplos do mundo real sobre agendamento de tarefas no Laravel.
- Laravel News: Advanced Laravel Scheduling — Artigo técnico abordando técnicas avançadas como tarefas dinâmicas e integração com serviços externos.
- DigitalOcean: How to Use Laravel Task Scheduler — Guia passo a passo para configurar e gerenciar tarefas agendadas em servidores Linux.
- Laracasts: Task Scheduling — Screencast detalhado sobre agendamento de tarefas, ideal para iniciantes no Laravel.
- Stack Overflow: Laravel Scheduler Best Practices — Discussões da comunidade sobre boas práticas, solução de problemas e casos de uso avançados.