Server-Sent Events en Rust: enviando datos en tiempo real sin WebSocket

Entrenador de Python en Línea para Principiantes

Aprende Python fácilmente sin sobrecargarte de teoría. Resuelve tareas prácticas con verificación automática, recibe pistas y escribe código directamente en el navegador, sin instalar nada.

Start Course

Cuando WebSocket es demasiado, o por qué SSE vuelve a estar de moda

\n\n

Imagine: necesita enviar datos del servidor al cliente en tiempo real. Tipos de cambio, notificaciones de nuevos mensajes, registros del servidor, progreso del procesamiento de archivos. Lo primero que viene a la mente es WebSocket. Pero, ¿qué pasa si no necesita un canal bidireccional? ¿Qué pasa si el cliente solo escucha y el servidor solo habla?

\n\n

Aquí entran en escena los Server-Sent Events (SSE): un protocolo que permite al servidor enviar datos al cliente a través de una conexión HTTP normal. Sin handshakes adicionales, sin protocolos complejos, sin dolores de cabeza con proxies y balanceadores de carga. Y sí, en Rust funciona de maravilla.

\n\n

En este artículo, analizaremos cómo implementar SSE en Rust desde cero, utilizando frameworks populares. Verá ejemplos funcionales, conocerá los obstáculos y comprenderá cuándo SSE es su mejor opción.

\n\n

¿Qué son los Server-Sent Events y por qué Rust?

\n\n

SSE es un estándar W3C (API EventSource) que permite al servidor enviar datos en flujo al cliente a través de HTTP. El cliente se conecta a un endpoint y el servidor comienza a enviar mensajes en formato text/event-stream. Todo funciona sobre HTTP/1.1 o HTTP/2 normal, no requiere proxies especiales y es compatible con todos los navegadores modernos.

\n\n

¿Por qué Rust? Porque Rust nos ofrece:

\n
    \n
  • Asincronía sin sorpresas — Tokio y async-std permiten mantener miles de conexiones SSE en un solo hilo
  • \n
  • Seguridad de memoria — sin fugas, sin condiciones de carrera en transmisión multihilo
  • \n
  • Consumo mínimo de recursos — una conexión SSE consume unos pocos kilobytes
  • \n
  • Ecosistema — Actix Web, Rocket, Axum: todos soportan SSE de fábrica o con extensiones mínimas
  • \n
\n\n

Cómo funciona SSE: analizando el protocolo

\n\n

El cliente crea un EventSource en el navegador, especificando una URL. El servidor responde con el encabezado Content-Type: text/event-stream y comienza a enviar líneas en el formato:

\n\n
data: ¡Hola, mundo!\\n\\n
\n\n

Cada mensaje es un bloque donde data: contiene la carga útil. Los mensajes se separan por dos caracteres de nueva línea. Se puede transmitir JSON, números, cadenas, lo que sea. Adicionalmente, existen los campos id, event y retry.

\n\n

Aquí hay un ejemplo mínimo de un mensaje SSE:

\n\n
id: 1\nevent: update\ndata: {"status": "processing", "progress": 42}\n\n
\n\n

El cliente en JavaScript recibe esto como un evento update con el campo data que contiene el JSON.

\n\n

Escribiendo un servidor SSE en Rust con Actix Web

\n\n

Empecemos con el framework más popular: Actix Web. Instalemos las dependencias:

\n\n
[dependencies]\nactix-web = "4"\nactix-rt = "2"\ntokio = { version = "1", features = ["full"] }\nserde = { version = "1", features = ["derive"] }\nserde_json = "1"
\n\n

Ahora escribamos un endpoint simple que envíe la hora actual cada 2 segundos:

\n\n
use actix_web::{get, web, App, HttpRequest, HttpResponse, HttpServer, middleware};\nuse actix_web::rt::time;\nuse futures::stream::StreamExt;\nuse std::time::Duration;\n\n#[get("/events")]\nasync fn stream_events(req: HttpRequest) -> HttpResponse {\n    let mut rx = tokio::sync::broadcast::channel::<String>(100).1;\n\n    let stream = async_stream::stream! {\n        loop {\n            let message = format!("data: {}\\n\\n", chrono::Local::now());\n            yield Ok::<_, actix_web::Error>(\n                web::Bytes::from(message)\n            );\n            time::sleep(Duration::from_secs(2)).await;\n        }\n    };\n\n    HttpResponse::Ok()\n        .insert_header(("Content-Type", "text/event-stream"))\n        .insert_header(("Cache-Control", "no-cache"))\n        .insert_header(("Connection"                

Blogs

Book Recommendations