Skip to content

SSE with Spring Boot — Complete Guide to Reactive Streaming

DodaTech Updated 2026-06-28 4 min read

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

  1. What is SseEmitter and how does it work in Spring Boot?
  2. Why should SSE emitters not block the servlet thread?
  3. How does WebFlux SSE differ from SseEmitter?
  4. Why are cleanup handlers important for SseEmitter?
  5. What is the default SseEmitter timeout?

Answers:

  1. SseEmitter is a Spring class that holds an async HTTP connection for sending SSE events.
  2. Blocking the servlet thread prevents it from handling other requests; use async task executors.
  3. WebFlux provides reactive backpressure and is fully non-blocking; SseEmitter is simpler but blocking.
  4. Without cleanup, disconnected clients leave orphaned emitters that consume memory and may cause errors.
  5. 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

Does Spring Boot support SSE without WebFlux?

: Yes, SseEmitter works with traditional Spring MVC.

How many SSE connections can Spring Boot handle?

: Limited by available threads with SseEmitter; WebFlux can handle 10,000+ concurrent connections.

Can SseEmitter send binary data?

: No, SseEmitter sends text only. Encode binary as Base64 for SSE.

How do you handle CORS with Spring Boot SSE?

: Use @CrossOrigin annotation or a WebMvcConfigurer to set CORS headers.

What is the difference between SseEmitter and ResponseBodyEmitter?

: SseEmitter extends ResponseBodyEmitter with SSE-specific formatting (event:, data:, id:).

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