Colas de tareas para Rust: revisión completa de herramientas y bibliotecas
Rust es un lenguaje de programación de sistemas que está ganando popularidad en el desarrollo web y la creación de microservicios de alta carga. Uno de los requisitos clave para las aplicaciones backend modernas es el procesamiento asíncrono de tareas en segundo plano: envío de correos electrónicos, generación de informes, procesamiento de archivos cargados o interacción con APIs externas. En este artículo, veremos cómo organizar una cola de tareas en Rust, qué bibliotecas existen para ello y cómo aplicarlas en la práctica.
¿Por qué es necesaria una cola de tareas?
En una aplicación síncrona, cada operación se ejecuta secuencialmente. Si un usuario inicia una tarea larga (por ejemplo, procesamiento de video), el servidor se bloqueará hasta que finalice. La cola de tareas resuelve este problema: las tareas se colocan en un broker (cola), y workers especializados las toman y ejecutan de forma asíncrona. Esto permite:
- Descargar el hilo principal de la aplicación;
- Escalar el procesamiento horizontalmente (agregar workers);
- Garantizar la ejecución de la tarea incluso en caso de fallos (gracias a confirmaciones y reintentos).
Bibliotecas populares para organizar colas en Rust
El ecosistema de Rust ofrece varias soluciones maduras. Consideremos tres enfoques principales.
1. Celery-rs (Rust Celery)
Celery-rs es una implementación en Rust del popular Python Celery. La biblioteca permite usar brokers Redis o RabbitMQ. Es ideal si estás migrando de Python a Rust o deseas compatibilidad con una infraestructura Celery existente.
Ejemplo de un worker básico:
use celery::Celery;use celery::task::Task;
#[celery::task]fn add(x: i32, y: i32) -> i32 { x + y}
#[tokio::main]async fn main() -> Result<(), Box<dyn std::error::Error>> { let celery = Celery::builder("redis://localhost:6379/0") .with_task(add) .build().await?;
// Iniciar worker celery.run().await?;
// Enviar tarea let task_id = add.delay(&celery, 1, 2).await?; println!("ID de tarea: {}", task_id);
Ok(())}Ventajas: API familiar para desarrolladores de Python, soporte para escenarios complejos (retry, ETA, tareas cordales).
Desventajas: dependencia de Redis/RabbitMQ, un poco pesado para casos simples.
2. Lapin + Tokio (trabajo directo con RabbitMQ)
Lapin es un cliente AMQP de alto rendimiento para Rust, que funciona con Tokio. Si necesitas control total sobre colas, exchanges y routing keys, Lapin es la mejor opción. No proporciona abstracciones de tareas "listas para usar", pero ofrece flexibilidad.
Ejemplo de envío y recepción de mensajes:
use lapin::{options::*, types::FieldTable, Connection, ConnectionProperties};use tokio_amqp::*;
#[tokio::main]async fn main() -> Result<(), Box<dyn std::error::Error>> { let conn = Connection::connect( "amqp://guest:guest@localhost:5672", ConnectionProperties::default().with_tokio(), ).await?;
let channel = conn.create_channel().await?; let queue = channel.queue_declare( "task_queue", QueueDeclareOptions::default(), FieldTable::default(), ).await?;
// Enviar tarea let payload = b"¡Hola, tarea!"; channel.basic_publish( "", "task_queue", BasicPublishOptions::default(), payload, BasicProperties::default(), ).await?;
// Recibir tarea (worker) let consumer = channel.basic_consume( "task_queue", "consumer", BasicConsumeOptions::default(), FieldTable::default(), ).await?;
while let Some(delivery) = consumer.next().await { let delivery = delivery?; println!("Tarea recibida: {:?}", delivery.data); delivery.a