Voltar ao blog
laravelphpqueuesbackendperformance

Quando usar filas no Laravel: casos reais

Se o seu sistema Laravel está lento em determinadas operações, envio de e-mail, geração de relatório, integração com API externa, você provavelmente está executando trabalho pesado dentro do ciclo de requisição HTTP. O usuário clica, espera, espera… e às vezes a requisição expira.

A solução para esse problema, na maioria dos casos, chama-se fila, ou queue.

O que é uma queue no Laravel

Uma queue é uma lista de tarefas que serão executadas em segundo plano, fora do ciclo da requisição HTTP. Em vez de o usuário esperar o sistema enviar um e-mail ou chamar uma API de terceiro, você joga esse trabalho numa fila e retorna a resposta imediatamente.

O Laravel tem suporte nativo a vários drivers: banco de dados, Redis, Amazon SQS, Beanstalkd. Para começar, o driver database já serve bem e não exige nenhuma infra extra. Você cria a tabela de Jobs com php artisan queue:table && php artisan migrate e já tem uma fila funcional.

A mecânica é simples. A requisição HTTP cria um Job e coloca na fila. Um processo separado, o worker, consome essa fila e executa os Jobs. O usuário não precisa esperar.

Os casos onde queues resolvem de verdade

Envio de e-mail

O mais comum. Enviar um e-mail via SMTP pode levar de 1 a 5 segundos dependendo do provedor. Dentro de uma requisição, o usuário sente esse atraso inteiro.

// sem queue: usuário espera o e-mail ser enviado
Mail::to($user->email)->send(new BemVindoMail($user));

// com queue: retorno imediato, e-mail enviado em background
Mail::to($user->email)->queue(new BemVindoMail($user));

A mudança é mínima no código. O impacto na experiência do usuário é imediato.

Integração com APIs externas

Você fecha um pedido e precisa notificar um ERP, um CRM, ou um sistema de logística. Essas APIs podem estar lentas, fora do ar ou com timeout variável. Jogar essa integração numa queue resolve dois problemas ao mesmo tempo: o usuário não espera e você ganha retry automático em caso de falha.

class NotificarErpJob implements ShouldQueue
{
    use Dispatchable, InteractsWithQueue, Queueable, SerializesModels;

    public int $tries = 3;
    public int $backoff = 60;

    public function __construct(private Pedido $pedido) {}

    public function handle(ErpService $erp): void
    {
        $erp->notificarNovoPedido($this->pedido);
    }
}

// no controller
NotificarErpJob::dispatch($pedido);

Com $tries = 3 e $backoff = 60, o Laravel tenta até três vezes antes de marcar o Job como falho, com 60 segundos de intervalo entre cada tentativa. Isso significa que uma instabilidade temporária na API do ERP não quebra o pedido do cliente.

Geração de relatórios e exportações

Relatórios que processam milhares de registros, geram PDFs ou exportam CSVs pesados não devem rodar dentro de uma requisição HTTP. O padrão certo: o usuário solicita, o sistema responde “seu relatório está sendo gerado”, o worker processa, salva o arquivo e notifica o usuário por e-mail ou atualiza a interface.

class GerarRelatorioJob implements ShouldQueue
{
    public int $timeout = 300;

    public function handle(): void
    {
        $dados = Pedido::with('itens')
            ->whereBetween('created_at', [$this->inicio, $this->fim])
            ->get();

        $caminho = "relatorios/{$this->usuario->id}/pedidos-{$this->inicio}.csv";
        Storage::put($caminho, $this->gerarCsv($dados));

        $this->usuario->notify(new RelatorioDisponivel($caminho));
    }
}

Processamento de imagens e arquivos

Upload de imagem seguido de redimensionamento, geração de thumbnail ou compressão vai no mesmo padrão. O usuário faz o upload, você salva o arquivo original e agenda o processamento. A interface mostra o preview do arquivo original enquanto o Job processa as versões otimizadas.

Webhooks recebidos

Quando seu sistema recebe um webhook de Stripe, PagSeguro ou Mercado Pago, você precisa responder com 200 OK em menos de 5 segundos. Se não responder a tempo, o provedor considera falha e tenta de novo, várias vezes, e você acaba processando o mesmo evento mais de uma vez.

O padrão certo: receba o payload, persista, jogue numa fila, retorne 200. O processamento real fica no Job.

public function handle(Request $request): Response
{
    $payload = $request->all();
    WebhookLog::create(['payload' => $payload]);

    ProcessarWebhookJob::dispatch($payload);

    return response()->json(['status' => 'ok']);
}

Quando não usar queue

Nem tudo precisa de fila. Adicionar uma queue onde não é necessário cria complexidade sem benefício.

Não use quando a operação é rápida (menos de 200ms) e o usuário precisa do resultado imediato. Não use quando você precisa retornar dados processados na mesma resposta HTTP. Em sistemas simples e MVPs, é comum começar sem queues e introduzir quando o problema de performance aparece de verdade. Otimização prematura tem custo de manutenção que acumula ao longo do projeto.

Como rodar os workers

Depois de criar os Jobs, você precisa de um processo para consumir a fila:

php artisan queue:work --queue=default --tries=3 --sleep=3

Em produção, use Supervisor para manter o worker sempre ativo e reiniciar automaticamente em caso de falha:

[program:laravel-worker]
command=php /var/www/html/artisan queue:work --sleep=3 --tries=3 --max-time=3600
autostart=true
autorestart=true
user=www-data
numprocs=2

Com numprocs=2 você tem dois workers rodando em paralelo. Útil quando o volume de Jobs é alto o suficiente para um único worker criar fila de espera.

Laravel Horizon vale a pena?

Se você usa Redis como driver, sim. O Horizon oferece um dashboard visual com Jobs em execução, pendentes e falhos, throughput por minuto e tempo médio de processamento. Configurar alertas para quando a fila crescer além de um limite é uma das features mais úteis.

composer require laravel/horizon
php artisan horizon:install
php artisan horizon

Para projetos em produção com volume relevante de Jobs, o Horizon paga o custo de configuração rapidamente. Para projetos menores com driver database, o php artisan queue:failed e as tabelas do próprio banco já são suficientes para o dia a dia.


Gabriel Schunck está disponível para projetos de software sob medida e consultorias. Se você tem um sistema com gargalos de performance ou quer estruturar um projeto novo com as práticas certas desde o início, entra em contato pelo gabriels.dev.br.

Falar no WhatsApp