Stack: DRF + ASGI · Proyecto: TicketFlow Estado: Publicada Prerrequisito: Lección 15 — Validación y OpenAPI
Objetivos
- Comparar REST/gRPC/GraphQL con el criterio de quién consume qué (y el coste de cada uno).
- Elegir entre WebSockets y SSE para la disponibilidad en vivo de TicketFlow.
- Implementar un endpoint SSE con Django ASGI y entender su implicación de concurrencia (la 03 reaparece).
1. El mapa de protocolos
| REST/JSON | gRPC | GraphQL | WS/SSE | |
|---|---|---|---|---|
| Modelo | recursos | RPC tipado | grafo consultable | push del servidor |
| Contrato | OpenAPI | .proto (binario) | schema SDL | evento |
| Cliente típico | todo el mundo | microservicio↔micro | frontend rico | tiempo real |
| Caché HTTP | sí (12) | no (HTTP/2 pero bodies binarios) | parcial (POST) | no |
| Curva | baja | media (tooling) | media-alta (N+1, perms) | media |
Criterio de elección: REST para la API pública (universo de clientes, caché, depuración con curl); gRPC interno (servicios propios: contratos binarios estrictos, streaming, rendimiento — cuando haya microservicios de verdad, Lección 53); GraphQL solo si el frontend necesita combinar muchos recursos con formas variables y el equipo paga su complejidad (N+1 en resolvers, permisos por campo); WS/SSE para empujar eventos.
La trampa de GraphQL: "un solo endpoint" suena a libertad y es mover el N+1 al servidor (dataloader obligatorio) y el control de acceso a cada campo (21). No es malo: es un coste continuo, no un sprint.
2. El caso TicketFlow: disponibilidad en vivo
El comprador mira el mapa de asientos; los asientos se venden bajo sus pies. Opciones:
- Polling:
GET /availabilitycada 3s — simple, HTTP cacheable (12), pero 1000 usuarios mirando = 333 rps de consultas idénticas (con caché Redis aguantable). - SSE (Server-Sent Events): el servidor empuja "asiento vendido" por un stream HTTP unidireccional — una conexión abierta por usuario, push en el momento. Reconnect nativo del navegador (EventSource), texto simple, proxy-friendly.
- WebSocket: canal bidireccional — si el usuario también emite (chat de soporte, subastas), sí; para "el servidor avisa", sobra.
Decisión de TicketFlow: SSE para disponibilidad (unidireccional, simple, HTTP/2 friendly) + polling con caché como fallback. WebSocket solo si aparece bidireccionalidad real (subastas).
3. SSE con Django ASGI (el código que abre la puerta)
# events/sse.py (consumidor en ASGI — Django con Uvicorn, la 03)
async def availability_stream(request, event_uuid):
event = await get_event(event_uuid) # sync_to_async
channel = f"avail:{event.id}"
pubsub = redis_client.pubsub()
await pubsub.subscribe(channel)
async for message in pubsub.listen():
if message["type"] == "message":
yield {"event": "availability", "data": message["data"]}Costes que debes gestionar: cada stream es una conexión abierta (1000 usuarios = 1000 conexiones — Uvicorn/ASGI las aguanta porque es I/O, la 03); keepalive cada ~15s para atravesar proxies (Nginx: proxy_buffering off en la ruta SSE); eventos y reconnection con Last-Event-ID para no perder ventas entre reconexiones; y el pub/sub (Redis publish desde el servicio que confirma la reserva → todos los streams del evento reciben el push).
4. Cuando NO conviene nada de esto
Si la disponibilidad cambia 10 veces por hora y el mapa se refresca al entrar, el polling con caché es suficiente y cero infraestructura nueva. Cada tecnología de push añade: conexiones persistentes (memoria/limites de LB), reconexiones (duplicados), y un nuevo failure mode. El push se justifica cuando el negocio exige el instante (subasta, subastas de entradas re-sell, colaboración), no porque "esté bien tenerlo en tiempo real".
Autoevaluación
- ¿Por qué REST sigue siendo la elección para API pública aunque gRPC sea "más rápido"?
- ¿Qué añade GraphQL como coste continuo y qué caso lo justifica?
- SSE vs WebSocket para disponibilidad: ¿qué decide la elección? ¿Cuándo WS gana?
- ¿Por qué el SSE necesita
proxy_buffering offen Nginx y keepalive? ¿Qué pasa sin ellos? - 1000 streams SSE abiertos: ¿qué recurso agota primero y cómo lo dimensionas (conecta con la 03/05)?
Continúa con los ejercicios. Las solutions.md solo tras intentarlo.