Optimización de la respuesta de backend: colas, async, backpressure
1) Por qué: objetivos y SLO
El objetivo es una respuesta rápida estable incluso bajo las ráfagas. El negocio lo expresa SLO:- API (CRUD/directorios): p95 ≤ 250-400 ms, error rate <1%.
- Page/settlment (asíncrono): SLA interno para la confirmación ≤ 2-5 min, y al cliente para la 202/Accepted instantánea + status-poller/webhook.
- WS/real-time: RTT p95 ≤ 120 мс, disconnect ≤ 0. 5%.
Clave: desata los pasos «lentos» (proveedores, DAB, APIs externas) de la respuesta del usuario a través de colas y limitación de carga competente.
2) Imagen básica: donde se toma latencia
Cuellos de botella: DB (grupos/índices), proveedores externos (PSP/juegos) que bloquean I/O, GC/stop-world, serialización JSON, agregaciones «pesadas».
Síntomas: crecimiento de p99, cola de conexiones a DB, ráfagas de retraídas, tiempo de espera por cadena (tormenta retry).
Antídoto: transportadores asíncronos + retroceso + tiempos de espera/retraídas + idempotencia.
3) Patrones asíncronos: SEDA y CQRS
SEDA (staged event-driven architecture): rompe el procesamiento en la etapa (ingress → validación → escritura → integración → notificación). Cada uno tiene su propia cola y límites de concurrencia.
CQRS: separar las lecturas y las escrituras. Escribir - en el registro/base, leer - de las proyecciones/cachés.
Outbox: el evento se publica atomicamente junto con la grabación (evitar mensajes «perdidos»).
Saga: largos procesos de negocio con transacciones compensatorias en lugar de globales.
4) Colas y estribos: selección y afinación
RabbitMQ/NATS JetStream - comandos de tareas (queues de trabajo), Kafka - eventos/streams con réplica.
Configuración que afecta a la respuesta:- Prefetch/max in-flight: limite el número de mensajes procesados simultáneamente por worker (por ejemplo, 16-64) para no «marcar» la DAB/API externa.
- Acker/repeticiones: 'ack' después de la grabación idempotente; repeticiones con retraso exponencial y jitter.
- DLQ/parking lot: no hay retiros sin punta - después de N intentos se va a Dead Letter Queue.
- Partición (Kafka): clave por entidad (userId/txnId) para ordenar; paralelismo a través del número de partidos.
5) Presión inversa (retroceso) - cómo no ahogarse
Idea: tomar sólo tanto como pueda procesar dentro de la latencia de SLO.
Técnicas:- Control de Admision: limite la competencia (semaphore/worker-pool) a cada dependencia externa: DB, PSP, proveedor de juegos.
- Tráfico Shaping: token-bucket/leaky-bucket en la entrada del servicio y en rutas críticas.
- Colas con límite superior: al rellenar, cortamos la cola (429/503 + Retry-After) o la transferimos a asap-batch.
- Concurrencia adaptativa (AIMD): aumente el paralelismo cuando tenga éxito, reduzca el tiempo de espera.
- Circuito Breaker: 'cerrado → abierto → medio abierto' por errores/tiempos de espera de la API externa; en abierto - degradación (cache/crupier).
go sem: = make (chan stract {}, 64 )//límite de competencia al DB/PSP
func handle(req) {
select {
case sem <- struct{}{}:
defer func(){ <-sem }()
ctx, cancel:= context. WithTimeout(req. ctx, 300time. Millisecond)
defer cancel()
res, err:= db. Do(ctx, req)
if err == context. DeadlineExceeded { metrics. Timeouts. Inc(); return TooSlow() }
return Ok(res)
default:
metrics. Backpressure. Inc()
return TooBusy(429, "Retry-After: 0. 2")
}
}6) Tiempo de espera, retraídas y jitter: «tres ballenas de supervivencia»
Los tiempos de espera son más cortos que SLO: si SLO 400 ms, el tiempo de espera al DB/proveedor es 250-300 ms; tiempo de espera total de la consulta <400-600 ms.
Retraídas limitadas e inteligentes: 1-2 intentos max, solo para operaciones seguras (idempotentes), con exponente y jitter.
Coalessing: agregue repeticiones para una sola clave.
Pseudocódigo (expositor + jitter):python for attempt in range(0, 2):
try:
return call(dep, timeout=0. 3)
except Timeout:
backoff = (0. 05 (2attempt)) + random. uniform(0, 0. 05)
sleep(backoff)
raise UpstreamUnavailable7) Idempotencia y deduplicación
Idempotency-Key en HTTP (depósitos, pagos), 'operation _ id' en DB (índice único).
Inbox/Outbox: webhooks entrantes - siempre a través de una tabla inbox inmutable con dedupe por 'event _ id'; saliente: de outbox por transacción.
Exactly-once «por sentido»: permitimos la entrega/ejecución repetida, pero el efecto es uno.
8) API rápida para operaciones lentas
Respuesta sincronizada: 201/202 + URL de estado ('/status/{ id} '), ETA y pistas de retroceso.
Webhooks/Server-Sent Events/WS - Push State cuando esté listo.
Disciplina del cliente: 'Retry-After', idempotencia, límite de encuesta.
Ejemplo de respuesta:json
HTTP/1. 1 202 Accepted
Location: /v1/withdrawals/req_9f2/status
Retry-After: 2
{
"request_id": "req_9f2",  "state": "processing",  "next_check_sec": 2
}9) Minimizar el trabajo en la ruta caliente
Lleve las cosas pesadas al fondo: conversiones, agregaciones, notificaciones, escritura en DWH.
Caché y proyecciones: a menudo legible - cache-aside con TTL corto y discapacidad de evento.
Patrones de batch: agrupe las llamadas externas (por ejemplo, solicitar límites de proveedor una vez en N ms).
Serialización: codecs rápidos (protobuf/msgpack) para conexiones entre servicios; JSON sólo en edge.
10) DAB bajo control
Agrupaciones de conexiones: los bordes superiores (basados en núcleos/IO), las colas del grupo están activadas.
Índices y plan: p95 explain + autotesta de regresión de planes.
Tiempos de espera de consulta: cortos, 'statement _ timeout' (Postgres).
Hot rows/locks: charding por clave, bloqueos optimistas (versión balance), saga en lugar de transacción «monolítica».
11) WebSocket/real-time
Limitador por boletín: broadcast batched, max msgs/sec per connection.
Backpressure interno: cola de mensajes salientes con goteo; en caso de desbordamiento, drop low-priority.
Sticky-routing y PDB en lanzamientos - para no fructificar una tormenta de reconnect.
12) Observabilidad para no adivinar
Métricas (RED/USE + backpressure):- 'request _ rate', 'error _ ratio', 'latency _ p95/p99' en las rutas.
- `queue_depth`, `lag_seconds`, `consumer_inflight`, `retries_total`, `dlq_rate`.
- `backpressure_drops`, `admission_rejects`, `circuit_open`.
- Для БД: `connections_in_use/max`, `locks`, `slow_queries`.
- Tracks: durmiendo 'queue → worker → db/psp' con las etiquetas 'operation _ id', 'partition', 'retry'.
- Logs: estructural, con 'trace _ id', sin PII; eventos individuales «circuito abierto/cerrado».
13) Pruebas bajo carga
Modelo abierto (arrivals/sec) para ráfagas; Modelo cerrado (VUs) para sesiones.
Perfiles: breve burst 60-120 s y soak 1-4 h.
Inyecciones de fallo: ralentice la API externa en + 200-500 ms, mire p99/retraídas/colas.
Criterios de zona verde: sin crecimiento 'queue _ lag', p95 estable, 'dlq_rate≈0'.
14) Seguridad y fiabilidad
Colas TLS/mTLS, firma de mensajes, control de esquema (Avro/Protobuf + Registro de Schema).
Idempotent producer (Kafka), exactly-once tx donde se justifica.
Modo Caos: periódicamente «rompe» la dependencia y mira la degradación (circuit, fallback).
15) Ejemplos de configuraciones de «piezas»
Nginx/Envoy entrada shaping:nginx limit_req_zone $binary_remote_addr zone=api:10m rate=20r/s;
server {
location /api/ {
limit_req zone=api burst=40 nodelay;
proxy_read_timeout 0. 6s; # más corto que SLO proxy_connect_timeout 0. 2s;
}
}
basic. qos (prefetch_count = 32) # balance de CPU/IOjava props. put(ConsumerConfig. MAX_POLL_RECORDS_CONFIG, 200);
props. put(ConsumerConfig. FETCH_MAX_BYTES_CONFIG, 5_000_000);
props. put(ConsumerConfig. MAX_POLL_INTERVAL_MS_CONFIG, 60_000);16) Lista de comprobación de la implementación (prod-ready)
- Las rutas críticas se dividen en respuesta sincrónica y procesamiento asíncrono (SEDA).
- Control de Admision y límites de competencia para las dependencias externas.
- Los tiempos de espera son más cortos que SLO; retraye ≤ 2, con exponente y jitter; Coalessing.
- Circuit breaker + degradación (cache/road), política half-open.
- Colas/streams: prefetch/in-flight, DLQ, lotes de llaves.
- Idempotencia (operation_id/Idempotency-Key), Outbox/Inbox, deduplicación.
- Caché: cache-aside, TTL corto + invalidez de evento.
- DB: límites de grupos, statement_timeout, índices, estrategias anti-bloqueo.
- WS: límites de mensajes, batching, sticky-routing, PDB.
- Observabilidad: métricas backpressure/queues/retries, tracks end-to-end, dashboards.
- Pruebas de carga y falla (open + closed, burst + soak), criterios de zona verde.
Resumen
El backend rápido no es «hacer un caché más», sino un flujo controlado: la entrada es limitada, el pesado está en el fondo, cada etapa con cola y límites, los retraídos son raros e inteligentes, y las cadenas están protegidas por el circuit breaker y la idempotencia. Agregue la disciplina del tiempo de espera, la observabilidad y las pruebas de estrés regulares - y sus p95/p99 permanecerán verdes incluso bajo los burstos y caprichos de los proveedores externos.
