JOBS Y QUEUES EN LARAVEL: CÓMO PROCESAR TAREAS EN SEGUNDO PLANO

En cualquier aplicación Laravel que crezca, llega un momento en el que ciertas operaciones no pueden bloquear la respuesta HTTP del usuario. Enviar emails masivos, procesar imágenes, sincronizar con APIs externas, generar reportes o indexar datos son tareas que deben ejecutarse en segundo plano. Ahí es donde entran en juego los Jobs y las Queues de Laravel siendo una de las características más poderosas y elegantes del framework.

¿Qué son las Queues?

Las Queues, o colas, permiten mover tareas pesadas fuera del ciclo de vida de la petición HTTP. En lugar de que el usuario espere 5-10 segundos mientras se envía un email o se procesa un vídeo, Laravel devuelve la respuesta inmediatamente y ejecuta la tarea de forma asíncrona. Su beneficio principales son
  • Mejora drástica del tiempo de respuesta.
  • Mejor experiencia de usuario.
  • Posibilidad de reintentar tareas fallidas automáticamente.
  • Escalabilidad horizontal: puedes tener múltiples workers procesando colas.
  • Separación clara entre lógica síncrona y asíncrona.

¿Qué es un Job en Laravel?

Un Job es una clase que representa una tarea específica que se puede poner en cola. Laravel genera estas clases con un comando artisan muy sencillo:

php artisan make:job SendWelcomeEmail
Ejemplo de un job de Laravel:

namespace App\Jobs;

use Illuminate\Contracts\Queue\ShouldQueue;
use Illuminate\Foundation\Queue\Queueable;

class SendWelcomeEmail implements ShouldQueue
{
use Queueable;

public $user;

public function __construct($user)
{
$this->user = $user;
}

public function handle()
{
// Lógica del job (enviar email, etc.)
Mail::to($this->user)->send(new WelcomeMail($this->user));
}
}
Al implementar "ShouldQueue", Laravel sabe que este job debe ir a la cola en lugar de ejecutarse inmediatamente. Los jobs se pueden despachar desde un controlador, evento, listener o incluso desde otro job:

// Despacho inmediato
SendWelcomeEmail::dispatch($user);

// Despacho retardado (ej: después de 10 minutos)
SendWelcomeEmail::dispatch($user)->delay(now()->addMinutes(10));

// Despacho después de que se envíe la respuesta HTTP (Laravel 11+)
SendWelcomeEmail::dispatch($user)->afterResponse();
También se puedes usar el helper global:

dispatch(new SendWelcomeEmail($user));

Configuración de las Queues

Los jobs y las colas se configuran en el archivo "config/queue.php". Los drivers más usados son:
  • database → Ideal para empezar (fácil de configurar y depurar).
  • redis → El más recomendado en producción por su velocidad y características (soporta Horizon).
  • sqs (Amazon SQS) → Perfecto si estás en AWS.
  • beanstalkd, sync (para testing) y null.
Para usar el driver de base de datos:

php artisan queue:table
php artisan migrate

Ejecutar los Workers

El comando principal para procesar las colas es:

php artisan queue:work
Opciones útiles:
  • "--queue=high, default"→ procesar varias colas por prioridad.
  • "--tries=3" → número máximo de intentos.
  • "--timeout=60" → tiempo máximo por job.
  • "--backoff=3" → segundos de espera antes de reintentar.
En producción se recomienda usar Supervisor para mantener los workers siempre activos.

Características avanzadas

  • Job Chaining (encadenamiento de jobs):

    ProcessPodcast::withChain([
    OptimizePodcast::class,
    ReleasePodcast::class,
    ])->dispatch($podcast);
    
  • Job Batching → Ejecutar un grupo de jobs y recibir notificación cuando todos terminen.
  • Rate Limiting en jobs (disponible desde Laravel 8+).
  • Middleware de Jobs → Puedes crear middlewares personalizados para logging, throttling, etc.
  • Failed Jobs → Laravel guarda automáticamente los jobs que fallan en la tabla failed_jobs. Se puede reintentar con:

    php artisan queue:retry all
    
  • Horizon (para Redis) → Un dashboard hermoso para monitorear colas en tiempo real, métricas, balanceo, etc.

Mejores prácticas

  • Usar Redis en producción siempre que sea posible es más rápido y con mejor soporte para prioridades.
  • Hacer que loa jobs que puedan ejecutarse varias veces sin efectos secundarios.
  • Mantener los jobs lo más ligeros posible. La lógica pesada debe ir en Actions o Services.
  • Para tareas muy pequeñas que solo necesitan ejecutarse después de la respuesta, considerar defer() (nuevo en Laravel 11).
  • Separar los workers del servidor web cuando la aplicación crezca.
  • Monitorizae siempre los failed jobs y configurar alertas.

Ejemplo real

Ejemplo con el código de procesamiento de imágenes. Primero crear el job con el comando Artisan::

php artisan make:job ProcessUploadedImage
Y ya añadir al fichero el código:

//app/Jobs/ProcessUploadedImage.php

namespace App\Jobs;// ← Este es el namespace estándar

use Illuminate\Contracts\Queue\ShouldQueue;
use Illuminate\Foundation\Queue\Queueable;

class ProcessUploadedImage implements ShouldQueue
{
use Queueable;

public function __construct(public $imagePath) {}

public function handle(): void// ← Recomendado agregar : void en versiones modernas
{
// Redimensionar, optimizar, subir a S3, etc.
ImageOptimizer::process($this->imagePath);
}
}
En el controlador:

ProcessUploadedImage::dispatch($path)
->onQueue('images')  // cola específica
->delay(now()->addSeconds(5));