Colas de tareas para Go (Golang): Revisión y comparación de herramientas
En el desarrollo web moderno y la creación de servicios de alta carga con Go (Golang), casi siempre surge la necesidad de ejecutar tareas de forma asíncrona, fuera del flujo principal de procesamiento de solicitudes HTTP. Envío de notificaciones por correo electrónico, generación de informes en PDF, procesamiento de imágenes, interacción con APIs externas: todo esto requiere ser trasladado a procesos en segundo plano. Aquí es donde entran en juego las colas de tareas (task queues).
Una cola de tareas es un mecanismo que permite retrasar la ejecución de una operación, garantizar su reprocesamiento en caso de fallo y equilibrar la carga entre varios workers. En el ecosistema de Go, existen varias bibliotecas maduras y en activo desarrollo para resolver esta tarea. En este artículo, analizaremos en detalle los tres enfoques más populares: Asynq, Machinery y el trabajo directo con Redis Streams.
1. Asynq: Simplicidad y potencia basada en Redis
Asynq, del desarrollador hibiken, es probablemente la biblioteca de colas de tareas más popular en Go en la actualidad. Está construida sobre Redis y ofrece una amplia funcionalidad "lista para usar".
Características clave de Asynq:
- Monitoreo: Interfaz web integrada (Asynqmon) para rastrear colas, ejecución de tareas y errores.
- Programación: Posibilidad de retrasar la ejecución de una tarea a un momento específico (sintaxis tipo cron).
- Prioridades: Soporte para múltiples colas con diferentes prioridades.
- Reintentos y Dead Letters: Reintentos automáticos y una cola de "mensajes muertos" para tareas irresolubles.
- Middleware: Soporte para middleware de registro, métricas, etc.
Ejemplo de uso de Asynq:
package main
import ( "context" "log" "time" "github.com/hibiken/asynq")
// Definimos el tipo de tareaconst TypeEmailDelivery = "email:deliver"
// Cliente para enviar tareasfunc main() { client := asynq.NewClient(asynq.RedisClientOpt{Addr: "localhost:6379"}) defer client.Close()
// Creamos una tarea task, err := asynq.NewTask(TypeEmailDelivery, []byte(`{"user_id": 42, "template": "welcome"}`)) if err != nil { log.Fatal(err) }
// Enviamos la tarea con un retraso de 5 minutos info, err := client.Enqueue(task, asynq.ProcessIn(5*time.Minute)) if err != nil { log.Fatal(err) } log.Printf("Tarea [%s] encolada", info.ID)}
// Worker para procesar tareasfunc handler(ctx context.Context, t *asynq.Task) error { log.Printf("Procesando tarea: %s", t.Type()) // Aquí va la lógica de envío de correo return nil}
func startWorker() { srv := asynq.NewServer( asynq.RedisClientOpt{Addr: "localhost:6379"}, asynq.Config{Concurrency: 10}, )
mux := asynq.NewServeMux() mux.HandleFunc(TypeEmailDelivery, handler)
if err := srv.Run(mux); err != nil { log.Fatal(err) }}Asynq es ideal para la mayoría de proyectos donde ya se utiliza Redis. Es fácil de configurar y proporciona una excelente experiencia de usuario para el desarrollador.
2. Machinery: Tareas distribuidas con brokers
Machinery es una biblioteca más antigua, pero muy potente, inspirada en la biblioteca de Python Celery. A diferencia de Asynq, Machinery no está estrictamente vinculado a Redis y admite múltiples brokers de mensajes: Redis, RabbitMQ, AWS SQS y Memory.
Características clave de Machinery:
- Flexibilidad de brokers: Cambia fácilmente entre diferentes brokers de mensajes.
- Resultados de tareas: Soporte integrado para guardar y obtener los resultados de la ejecución de una tarea (por ejemplo, en Redis).
También en la biblioteca
Creación de aplicaciones CLI en Node.js: frameworks Commander y Yargs
Monitoreo de aplicaciones TypeScript: bibliotecas, herramientas y configuración
JMH en Java: guía completa con ejemplos prácticos
Ktor Client Kotlin: guía completa con ejemplos prácticos