Los Server Sent Events (SSE) son una tecnología basada en HTTP que permite al servidor enviar datos al cliente de forma unidireccional y en tiempo real. A diferencia del modelo tradicional request/response, donde el cliente debe preguntar constantemente por nuevos datos (polling), con SSE el servidor mantiene la conexión abierta y envía información automáticamente cuando está disponible.
SSE está soportado de forma nativa por los navegadores modernos mediante la API EventSource, lo que lo convierte en una solución sencilla y eficiente para:
- Notificaciones en tiempo real
- Actualización de dashboards
- Logs en streaming
- Sistemas de monitorización
- Chats unidireccionales
La comunicación se realiza sobre HTTP estándar con el tipo de contenido:
text/event-streamCada evento sigue un formato simple:
event: nombreEvento
data: contenido
id: 1Alternativas: WebSockets
La alternativa más conocida a SSE es WebSockets, una tecnología que permite comunicación bidireccional full-duplex entre cliente y servidor sobre una única conexión TCP persistente.
Diferencias conceptuales
| Característica | SSE | WebSockets |
|---|---|---|
| Dirección | Unidireccional (Servidor → Cliente) | Bidireccional |
| Protocolo | HTTP estándar | ws / wss |
| Reconexión automática | Sí (nativa) | No (manual) |
| Complejidad | Baja | Media/Alta |
Tabla comparativa: Ventajas y Desventajas
| Tecnología | Ventajas | Desventajas |
|---|---|---|
| SSE | Simple de implementar. Basado en HTTP. Reconexión automática. Ideal para notificaciones | Solo unidireccional. No soporta binarios nativamente. Limitado en algunos proxies antiguos |
| WebSockets | Comunicación bidireccional. Mayor flexibilidad. Soporte binario | Mayor complejidad. Gestión manual de reconexión. No siempre compatible con infraestructuras legacy. |
Implementación clásica con Spring Boot 3
En Spring Boot 3, la forma tradicional de implementar SSE es mediante SseEmitter
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>/** Service **/
public class SSEService {
private Map<String, SseEmitter> emitters = new ConcurrentHashMap<>();
public SseEmitter subscribe(String userId) {
SseEmitter emitter = emitters
.computeIfAbsent(userId, k -> new SseEmitter(0L));
emitter.onCompletion(() -> {
log.info("Connection COMPLETED on USER: "+ userId);
remove(userId);});
emitter.onTimeout(() -> {
log.info("Connection TIMEOUT on USER: "+ userId);
remove(userId);});
emitter.onError(e -> {
log.info("Connection ERROR on USER: "+ userId);
remove(userId);});
objs.computeIfAbsent(userId, k -> obj);
return emitter;
}
public void remove(String userId) {
SseEmitter sse = emitters.get(userId);
if (sse != null) {
log.info("CLEAN CONNECTION AND REMOVE FILTER on USER: "+ userId);
emitters.remove(userId);
}
}
}/* Controller */
public class Controller {
@Autowired
private SSEService sseService;
@GetMapping(path = "/subscribe", produces = MediaType.TEXT_EVENT_STREAM_VALUE)
public SseEmitter subscribe(@RequestParam("userID") String userId) {
log.debug("Call to SSE Subscribe");
return service.subscribe(userId);
}
}Características clave
- Spring mantiene la conexión abierta
- El navegador reconecta automáticamente si se pierde
- Se puede personalizar timeout y gestión de errores
Avanzado
Programación Reactiva
En aplicaciones modernas, especialmente con alta concurrencia, el modelo bloqueante tradicional no es óptimo. Aquí entra la programación reactiva, basada en:
- Streams asíncronos
- Backpressure
- No bloqueo de hilos
Spring ofrece soporte mediante Project Reactor, usando tipos como:
Flux<T>Mono<T>
Para SSE, Flux es perfecto porque representa una secuencia de datos potencialmente infinita.
Uso con Spring WebFlux
Para entornos altamente concurrentes o arquitecturas basadas en microservicios, WebFlux es la opción recomendada.
Dependencia
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-webflux</artifactId>
</dependency>Implementación con Flux
/* Service */
@Service
public class SseService {
private final Map<String, Sinks.Many<String>> sinks = new ConcurrentHashMap<>();
public Sinks.Many<String> createSink(String clientId) {
return sinks.computeIfAbsent(clientId,
id -> Sinks.many().multicast().onBackpressureBuffer());
}
public void publish(String clientId, String message) {
Sinks.Many<String> sink = sinks.get(clientId);
if (sink != null) {
sink.tryEmitNext(message);
}
}
public Flux<ServerSentEvent<String>> subscribe(String clientId) {
Sinks.Many<String> sink = createSink(clientId);
Flux<ServerSentEvent<String>> flux = sink.asFlux()
.map(data -> ServerSentEvent.builder(data).build());
// heartbeat to keep connections alive
Flux<ServerSentEvent<String>> heartbeat = Flux.interval(Duration.ofSeconds(15))
.map(i -> ServerSentEvent.<String>builder().comment("heartbeat").build());
return Flux.merge(flux, heartbeat)
.doOnCancel(() -> sinks.remove(clientId));
}
}/* CONTROLLER */
@RestController
@RequestMapping("/sse")
public class SseController {
@Autowire
SseService sseService;
@GetMapping(value = "/stream", produces = MediaType.TEXT_EVENT_STREAM_VALUE)
public Flux<ServerSentEvent<String>> stream(@QueryParam String userId) {
return sseService.subscribe(userId)
}
}Esto es lo que sucede:
- Cada cliente obtiene su propio receptor (un publicador reactivo).
- Cuando el servidor llama a publish(clientId, message), ese mensaje se envía a todos los suscriptores.
- Los latidos garantizan que los proxies no interrumpan la conexión.
- Cuando un cliente se desconecta, el receptor se limpia.
Ventajas del enfoque reactivo
- No bloquea hilos
- Escala mejor con muchas conexiones abiertas
- Integración natural con bases de datos reactivas
- Soporte de backpressure
Conclusión
Los Server-Sent Events son una solución elegante y eficiente para escenarios donde el servidor necesita enviar información en tiempo real al cliente sin requerir comunicación bidireccional.
- Si necesitas simplicidad → SSE clásico con Spring MVC
- Si necesitas alta concurrencia y escalabilidad → SSE con WebFlux
- Si necesitas bidireccionalidad → WebSockets
En arquitecturas modernas basadas en microservicios y sistemas event-driven, SSE sigue siendo una herramienta extremadamente útil, especialmente cuando se combina con programación reactiva.
A lo mejor te interesa
What is HTTP Streaming and How Does it Work?
stomp-js/stompjs: Javascript and Typescript Stomp client for Web browsers and node.js apps
Stack para Testing (Swarm + jMeter)
Real-Time Applications with Server-Sent Events (SSE) in Spring Boot | by Ishara Madusanka | Medium

Leave a Reply
You must be logged in to post a comment.