SSE with Spring Boot — Complete Guide to Reactive Streaming
In this tutorial, you will learn about SSE with Spring Boot. We cover key concepts, practical examples, and best practices to help you master this topic.
SSE with Spring Boot uses SseEmitter and WebFlux to stream server-sent events from Java applications, supporting async processing and backpressure for scalable real-time data delivery.
What You'll Learn
- Implementing SSE with Spring Boot SseEmitter
- Using WebFlux for reactive SSE streaming
- Managing SSE connections and cleanup
Why It Matters
Spring Boot is one of the most popular Java frameworks for building APIs. SSE support through SseEmitter and WebFlux provides a straightforward path to real-time streaming without Websocket complexity.
Real-World Use
Durga Antivirus Pro Java-based threat analysis service uses Spring Boot SSE to stream analysis progress to the web dashboard. Each analysis session creates an SseEmitter that sends progress updates (25%, 50%, 75%, complete) in real time.
flowchart LR
C["Client"] --> S["Spring Boot Controller"]
S --> E["SseEmitter"]
E -->|"Event: progress"| C
E -->|"Event: complete"| C
E -->|"Event: error"| C
style E fill:#dbeafe,stroke:#2563eb
Code Examples
// Spring Boot SSE with SseEmitter
@RestController
public class SSEController {
@GetMapping("/events")
public SseEmitter streamEvents() {
SseEmitter emitter = new SseEmitter(3600000L); // 1 hour timeout
Executors.newSingleThreadExecutor().execute(() -> {
try {
for (int i = 0; i < 10; i++) {
Map<String, Object> data = new HashMap<>();
data.put("count", i);
data.put("timestamp", System.currentTimeMillis());
emitter.send(SseEmitter.event()
.name("update")
.data(data));
Thread.sleep(1000);
}
emitter.complete();
} catch (Exception e) {
emitter.completeWithError(e);
}
});
return emitter;
}
}
Expected output: Client receives 10 SSE events at 1-second intervals, then the stream completes.
// Reactive SSE with Spring WebFlux
@RestController
public class ReactiveSSEController {
@GetMapping(value = "/stream/metrics", produces = MediaType.TEXT_EVENT_STREAM_VALUE)
public Flux<ServerSentEvent<Map<String, Object>>> streamMetrics() {
return Flux.interval(Duration.ofSeconds(2))
.map(i -> {
Map<String, Object> metrics = new HashMap<>();
metrics.put("cpu", Math.random() * 100);
metrics.put("memory", Math.random() * 100);
metrics.put("timestamp", System.currentTimeMillis());
return ServerSentEvent.<Map<String, Object>>builder()
.event("metrics")
.id(String.valueOf(i))
.data(metrics)
.build();
});
}
}
Expected output: WebFlux reactive stream pushes metrics every 2 seconds with backpressure support.
// SSE emitter with timeout and error handling
@RestController
public class SSEWithCleanupController {
private final List<SseEmitter> emitters = new CopyOnWriteArrayList<>();
@GetMapping("/subscribe")
public SseEmitter subscribe() {
SseEmitter emitter = new SseEmitter(0L); // No timeout
emitter.onCompletion(() -> emitters.remove(emitter));
emitter.onTimeout(() -> emitters.remove(emitter));
emitter.onError(e -> emitters.remove(emitter));
emitters.add(emitter);
// Send initial connection event
try {
emitter.send(SseEmitter.event().name("connected").data("Subscribed"));
} catch (IOException e) {
emitter.completeWithError(e);
}
return emitter;
}
public void broadcast(String eventName, Object data) {
for (SseEmitter emitter : emitters) {
try {
emitter.send(SseEmitter.event().name(eventName).data(data));
} catch (IOException e) {
emitter.completeWithError(e);
emitters.remove(emitter);
}
}
}
}
Expected output: SSE emitters are tracked for broadcasting; cleanup handlers remove disconnected clients.
Common Mistakes
1. Not Setting a Timeout
Default SseEmitter timeout is 30 seconds. Set an appropriate timeout or 0L for long-lived connections.
2. Blocking the Servlet Thread
SSE emitters should be served from async threads, not the servlet container thread pool.
3. No Cleanup on Client Disconnect
Without onCompletion/onTimeout handlers, disconnected emitters accumulate and leak memory.
4. Sending Too Frequently
High-frequency SSE events can overwhelm clients. Use Flux.interval or throttling for controlled pacing.
5. Ignoring Backpressure with WebFlux
WebFlux handles backpressure automatically; SseEmitter does not. For high-throughput streams, prefer WebFlux.
Practice Questions
- What is SseEmitter and how does it work in Spring Boot?
- Why should SSE emitters not block the servlet thread?
- How does WebFlux SSE differ from SseEmitter?
- Why are cleanup handlers important for SseEmitter?
- What is the default SseEmitter timeout?
Answers:
- SseEmitter is a Spring class that holds an async HTTP connection for sending SSE events.
- Blocking the servlet thread prevents it from handling other requests; use async task executors.
- WebFlux provides reactive backpressure and is fully non-blocking; SseEmitter is simpler but blocking.
- Without cleanup, disconnected clients leave orphaned emitters that consume memory and may cause errors.
- 30 seconds. Set 0L for no timeout or a custom value in milliseconds.
Challenge: Build a Spring Boot SSE service that streams real-time stock prices. Use SseEmitter for simplicity, implement cleanup on disconnect, support named events (price_update, market_status), and broadcast to all connected clients.
FAQ
Mini Project
Build a Spring Boot SSE dashboard with: SseEmitter subscription endpoint, broadcast mechanism for pushing system metrics to all clients, WebFlux reactive stream endpoint for high-frequency data, and proper cleanup on disconnect and timeout.
What's Next
Learn about SSE with Django for Python-based SSE, or explore SSE performance optimization for scaling production deployments.
Built by the developers of DodaTech
Doda Browser, DodaZIP & Durga Antivirus Pro