BackendBench
Coffee

Unravel › Java concepts › APIs and web

Spring

Streaming responses and Server-Sent Events

Instead of answering once, a streaming endpoint keeps the HTTP connection open and sends several pieces over time: progress updates, live events, or a big file written as it is generated.

Learn these first

At a glance

Mental model

A radio broadcast versus a letter.

What happens, in order
  1. 1A method returning SseEmitter keeps the connection openOther code (a listener, a scheduled task) calls emitter.send(...) whenever there is news, and complete() at the end. The browser reads it with EventSource.
  2. 2StreamingResponseBody writes a large body (CSV export, file download) straight to the output stream, so the whole content never sits in memory
  3. 3DeferredResult and Callable free the servlet thread while a long operation is pending: the result is set from another thread and the response is completed later
  4. 4In WebFlux, return a Flux with media type text/event-stream and the framework does the same with backpressure
Remember
Watch out
Never removing finished emittersSending to an emitter from several threads without synchronisationNo heartbeat so proxies cut the streamLoading a whole export into memory instead of streaming it
Example (highlighted lines explain it)
@GetMapping("/orders/{id}/progress")public SseEmitter progress(@PathVariable String id) {    SseEmitter emitter = new SseEmitter(60_000L);    emitters.put(id, emitter);    emitter.onCompletion(() -> emitters.remove(id));    emitter.onTimeout(() -> emitters.remove(id));    return emitter;}
  1. line 2 uses SseEmitter, the core of Streaming responses and Server-Sent Events
  2. line 3 uses SseEmitter, the core of Streaming responses and Server-Sent Events
Read the full explanation (about 1 min)

Instead of answering once, a streaming endpoint keeps the HTTP connection open and sends several pieces over time: progress updates, live events, or a big file written as it is generated.

A mental model

A radio broadcast versus a letter. The letter arrives complete and the exchange is over; the broadcast stays tuned and keeps delivering.

How it works

A method returning SseEmitter keeps the connection open. Other code (a listener, a scheduled task) calls emitter.send(...) whenever there is news, and complete() at the end. The browser reads it with EventSource.

StreamingResponseBody writes a large body (CSV export, file download) straight to the output stream, so the whole content never sits in memory.

DeferredResult and Callable free the servlet thread while a long operation is pending: the result is set from another thread and the response is completed later.

In WebFlux, return a Flux with media type text/event-stream and the framework does the same with backpressure.

Going deeper

Keep a registry of live emitters and remove them on onCompletion, onTimeout and onError, otherwise closed browser tabs leak memory and every broadcast fails for them.

Each open stream holds a connection (and in blocking MVC, sometimes a thread). Thousands of idle streams need either WebFlux, virtual threads or a dedicated gateway.

Proxies and load balancers close idle connections, so send a small heartbeat comment every 15 to 30 seconds and let clients reconnect with Last-Event-ID.

Under the hood

Servlet async support (request.startAsync()) detaches the request from its container thread; the response is committed and flushed incrementally. Spring wraps this in ResponseBodyEmitter, which buffers sends until the handler returns and then writes directly.

Common mistakes

  • Never removing finished emitters.
  • Sending to an emitter from several threads without synchronisation.
  • No heartbeat so proxies cut the stream.
  • Loading a whole export into memory instead of streaming it.

How Unravel recognises it

When you paste code, Unravel looks for these signs of streaming responses and server-sent events. Spotting them yourself is a good habit when you read unfamiliar code:

Part of a learning path

Related concepts

Frequently asked questions

What is Streaming responses and Server-Sent Events?

Instead of answering once, a streaming endpoint keeps the HTTP connection open and sends several pieces over time: progress updates, live events, or a big file written as it is generated.

What are the most common mistakes with Streaming responses and Server-Sent Events?

Never removing finished emitters. Sending to an emitter from several threads without synchronisation. No heartbeat so proxies cut the stream. Loading a whole export into memory instead of streaming it.

How can I see Streaming responses and Server-Sent Events in real code?

Paste your own Java into Unravel and it will point out where streaming responses and server-sent events appears, explain how it works at the detail level you choose, and draw the flow of the code around it.