Colas de tareas y equilibrio
1) Por qué colas de tareas
La cola de tareas (job queue/work queue) desconecta a los fabricantes y ejecutantes en tiempo y velocidad:- Suaviza los picos: un búfer entre el frente y los subsistemas pesados.
- Estabiliza el SLA: prioridades y aislamiento de clases de carga.
- Simplifica la tolerancia a fallas: retraídas, DLQ, re-producción.
- Escala horizontal: agregue workers sin cambiar la API.
Dominios tipo: procesamiento de pagos, notificación, generación de informes/medios, postprocesamiento ETL/ML, integración con APIs externas.
2) Modelo y conceptos básicos
Productor: publica la tarea (payload + metadatos: idempotency key, prioridad, deadline).
Cola/topic: buffer/registro de tareas.
Worker: toma una tarea, procesa, confirma (ack) o devuelve con un error.
Visibility Timeout/Lease: «alquilar» una tarea durante el tiempo de procesamiento, después de - auto-raro.
DLQ (Dead Letter Queue): «enterrar» las tareas después de un límite de intentos/errores fatales.
Rate Limit/Concurrency: restricciones al consumo per-worker/peer-cola/per-tenant.
- Pull: el propio worker solicita la tarea (dosifica la carga).
- Push: el broker está disparando; necesita protección contra el «relleno» de los trabajadores débiles.
3) Semánticas de envío y confirmaciones
At-most-once: sin retrayas; más rápido, pero la pérdida es posible.
At-least-once (default para la mayoría de las colas): es posible duplicar → se requiere idempotencia del manejador.
Effectively exactly-once: se logra a nivel de aplicación (idempotencia, dedust stor, transacciones/outbox). Un corredor puede ayudar, pero no una «bala mágica».
- Ack/Nack: resultado claro.
- Requeue/Retry: с backoff + jitter.
- Mensaje de Poison: enviar a DLQ.
4) Equilibrio y planificación
4. 1 Orden de prioridad y algoritmos
FIFO: simple y previsible.
Priority Queue: clases prioritarias (P0... P3).
WRR/WSR (Weighted Round-Robin/Random): fracciones de CPU/cráneo entre clases.
WFQ/DRR (análogo de las colas «justas» en las redes): acciones per-tenant/cliente.
Deadline/EDF: para tareas con deadline.
Compartir feria: restringir los «vecinos ruidosos» (quotas per-tenant).
4. 2 Flujos de procesamiento
Single-flight/Coalescing: combina tareas duplicadas por clave.
Concurrency caps: límites duros para el paralelismo por tipo de tareas/integraciones (APIs externas).
4. 3 Geo y charding
Bolardos por clave (tenant/id) → la localidad de los datos, orden estable dentro de los chardos.
Sticky Cacham/Resources: Enrutamiento de hash en workers con estado «adjunto».
5) Retrai, backoff y DLQ
Backoff + jitter exponencial: 'base 2 ^ attempt ± random'.
Máximo de intentos y tiempo de espera total (time-to-die) por tarea.
Clasificación de errores: 'retryable' (red/límite), 'no retryable' (validación/prohibición empresarial).
Parking/Delay Queue: tareas retrasadas (por ejemplo, repetir después de 15 min).
Política DLQ: asegúrese de especificar dónde y en qué condiciones llega el mensaje «venenoso»; proporcione un reprocessor.
6) Idempotencia y deduplicación
Idempotency-Key en la tarea; stor (Redis/DB) con TTL para las últimas N claves:- seen → skip/merge/result-cache.
- Llaves naturales: utilice 'order _ id/ payment_id' en lugar de UUID aleatorios.
- Outbox: registra el hecho de una tarea y su estado en una sola transacción de BD con una operación comercial.
- Exactly-once en azul: 'UPSERT' por clave, verificador de versiones, 'at-least-once' en cola + idempotencia en CD.
7) Multi-tenencia y clases SLA
Divide las colas/streams por clases: 'critical', 'standard', 'bulk'.
Cuotas y prioridades per-tenant (Oro/Plata/Bronce).
Aislamiento: grupos de trabajo dedicados bajo P0; de fondo - en un clúster/nodos separados.
Control de Admision: no tomar más de lo que se puede procesar en los deduplines.
8) Workers Auto Scaling
Métricas para skaling: queue depth, arrival rate, processing time, SLA-deadline.
KEDA/Horizontal Pod Autoscaler: desencadenantes en profundidad SQS/Rabbit/Kafka lag.
Los factores disuasorios: APIs externos de rate limits, base de datos (no destruir back-end).
9) Opciones y patrones tecnológicos
9. 1 RabbitMQ/AMQP
Exchanges: direct/topic/fanout; Queues с ack/ttl/DLQ (dead-letter exchange).
Prefetch (QoS) ajusta «cuántas tareas hay en el worker».
ini x-dead-letter-exchange=dlx x-dead-letter-routing-key=jobs.failed x-message-ttl=60000
9. 2 SQS (y análogos)
Visibility Timeout, DelaySeconds, RedrivePolicy (DLQ).
Idempotencia - en la aplicación (tabla de dedoup).
Límites: batch 1-10 mensajes; centre en los azules idempotentes.
9. 3 Kafka/NATS JetStream
Para paipelines a gran escala: alto ancho de banda, retransmisión/retransmisión.
Cola de tareas encima de los registros: una tarea = un mensaje; control «un worker por llave» a través del lote/subject.
Retraies: topics/subject-sufijos individuales con backoff.
9. 4 colas Redis (Sidekiq/Resque/Bull/Celery-Redis)
Muy baja latencia; vigile la estabilidad (RDB/AOF), las llaves retry y las llaves de bloqueo para un solo vuelo.
Adecuado para tareas «fáciles», no para retenciones a largo plazo.
9. 5 Marcos
Celery (Python), Sidekiq (Ruby), RQ/BullMQ (Node), Huey/Resque - retiros terminados, horarios, middleware, métricas.
10) Esquemas de enrutamiento y equilibrio
Round-Robin: uniforme, pero no tiene en cuenta la «gravedad» de las tareas.
Weighted RR: distribución por capacidad de workers/pool.
Fair/Backpressure-aware: un worker sólo toma una nueva tarea cuando está listo.
Priority lanes: colas individuales por clase; Los trabajadores leen en orden [P0→... →Pn] si están disponibles.
Hash-routing: 'hash (key)% shards' - para el procesamiento en caché/stateful.
11) Timeouts, Deadline y SLA
Per-task timeout: «rent» interno del trabajo (en el código del worker) ≤ Visibility Timeout del bróker.
Global deadline: una tarea no tiene sentido después del tiempo T - NACK→DLQ.
Budget-aware: reduzca el trabajo (brownout) cuando se acerque la línea de salida (resultados parciales).
12) Observabilidad y gestión
12. 1 Métricas
`queue_depth`, `arrival_rate`, `service_rate`, `lag` (Kafka), `invisible_messages` (SQS).
`success/failed/retired_total`, `retry_attempts`, `dlq_in_total`, `processing_time_ms{p50,p95,p99}`.
`idempotency_hit_rate`, `dedup_drops_total`, `poison_total`.
12. 2 Registros/trayecto
Correlación: 'job _ id', 'correlation _ id', clave de deduplicación.
Marque 'retry/backoff/dlq' como eventos; link desde el span de la solicitud original.
12. 3 Dashboards/alertas
Disparadores: profundidad> X, p99> SLO, crecimiento de DLQ, tareas «vertidas» (visibility caducado> N), claves «calientes».
13) Seguridad y cumplimiento
Aislamiento de inquilinos: colas individuales/espacios clave, LCA, cuotas.
Encriptación en el transporte y/o «en reposo».
PII-minimización en payload; hash/ID en lugar de PII crudo.
Secretos: no poner tokens en el cuerpo de las tareas, utilizar vault/refs.
14) Anti-patrones
Retraídas sin idempotencia → tomas de operaciones/dinero «dos veces».
Una cola gigante «para todo» → no hay aislamiento, demoras impredecibles.
Los retratos infinitos sin DLQ → tareas eternas «venenosas».
Visibility Timeout <tiempo de procesamiento → duplicados en cascada.
El payload grande en la cola → presiona la red/la memoria; es mejor almacenar en el emisor de objetos y pasar el enlace.
El modelo de push sin retroceso → los workers se atragantan.
Mezcla de tareas críticas y de bulk en un solo grupo de workers.
15) Lista de verificación de implementación
- Clasifique las tareas por SLA (P0/P1/P2) y volumen.
- Seleccione el bróker/framework con la semántica y el retoque deseados.
- Diseñe claves, prioridades y enrutamiento (hash/shards/priority lanes).
- Habilite los retiros con backoff + jitter y la política DLQ.
- Implemente la idempotencia (llaves, upsert, dedup stor con TTL).
- Configure los temporizadores: per-task, visibility, general deadline.
- Limitar la concurrencia y la tasa por integraciones/tenantes.
- Auto Skaling por profundidad/laguna con fusibles.
- Métricas/senderismo/alertas; runbooks en «tormenta» y desbordamiento de DLQ.
- Pruebas de Feil: caída del worker, mensaje «venenoso», sobrecarga, tareas largas.
16) Ejemplos de configuraciones y código
16. 1 Celery (Redis/Rabbit) - flow básico
python app = Celery("jobs", broker="amqp://...", backend="redis://...")
app.conf.task_acks_late = True # ack после выполнения app.conf.broker_transport_options = {"visibility_timeout": 3600}
app.conf.task_default_retry_delay = 5 app.conf.task_time_limit = 300 # hard timeout
@app.task(bind=True, autoretry_for=(Exception,), retry_backoff=True, retry_jitter=True, max_retries=6)
def process_order(self, order_id):
if seen(order_id): return "ok" # идемпотентность do_work(order_id)
mark_seen(order_id)
return "ok"
16. 2 RabbitMQ — DLQ/TTL
ini x-dead-letter-exchange=dlx x-dead-letter-routing-key=jobs.dlq x-message-ttl=600000 # 10 минут x-max-priority=10
16. 3 Kafka - retraídas por niveles
orders -> orders.retry.5s -> orders.retry.1m -> orders.dlq
(Transfiera con entrega diferida a través de scheduler/cron-consumer.)
16. 4 NATS JetStream — consumer с backoff
bash nats consumer add JOBS WORKERS --filter "jobs.email" \
--deliver pull --ack explicit --max-deliver 6 \
--backoff "1s,5s,30s,2m,5m"
17) FAQ
P: ¿Cuándo elegir push vs pull?
R: Pull da un backpressure natural y un equilibrio «honesto»; push es más fácil a bajas velocidades y cuando se necesita un mínimo TTFB, pero requiere limitadores.
P: ¿Cómo evitar una llave «caliente»?
R: Barre por clave compuesta ('order _ id% N'), almacene en búfer y haga un procesamiento de batch, escriba límites por clave.
P: ¿Es posible «exactly-once»?
R: Prácticamente - a través de la idempotencia y el outbox transaccional. Totalmente «matemático» exactly-once en todo el camino es raramente alcanzable y caro.
P: ¿Dónde almacenar los archivos adjuntos grandes de la tarea?
R: En el almacén de objetos (S3/GCS), y en la tarea, una referencia/ID; reduce la presión sobre el corredor y la red.
P: ¿Cómo elegir TTL/visibilidad?
A: Visibility ≥ p99 tiempo de procesamiento × stock 2-3 ×. Tareas TTL - Menos tiempo de espera de negocios.
18) Resultados
Un sistema de colas fuerte es el equilibrio entre la semántica de entrega, las prioridades y los limitantes. Diseñe claves y enrutamiento, proporcione idempotencia, retrés con backoff y DLQ, asigne recursos a clases de SLA y supervise las métricas. Entonces sus procesos de fondo serán predecibles, sostenibles y escalables, sin sorpresas bajo los picos.