Using Spring's ResponseBodyEmitter for Real-Time Streaming Responses
This article explains how Spring's ResponseBodyEmitter, introduced in Spring 4.2, enables lightweight asynchronous HTTP streaming for use cases like real-time logs, progress updates, chat, and AI responses, and provides a complete code example with best-practice guidelines.
Introduction
ChatGPT's popularity has brought streaming output techniques to the forefront, and many developers started exploring SSE asynchronous handling. This article introduces a simpler way to achieve streaming output using Spring's ResponseBodyEmitter.
What is ResponseBodyEmitter
ResponseBodyEmitter, introduced in Spring Framework 4.2, is an interface that enables asynchronous HTTP responses by allowing data to be sent to the client incrementally rather than all at once. It is especially useful for long-running or streaming scenarios.
Typical Use Cases
Long polling
Server‑Sent Events (SSE)
Streaming large data (file download, real‑time feeds)
Asynchronous processing where partial results are returned progressively.
Practical Example – Real‑Time Log Streaming
The following Spring Boot controller demonstrates how to create a real‑time log stream using ResponseBodyEmitter.
import org.springframework.http.MediaType;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.servlet.mvc.method.annotation.ResponseBodyEmitter;
@RestController
@RequestMapping("/api/log")
public class LogController {
@GetMapping(value = "/stream", produces = MediaType.TEXT_EVENT_STREAM_VALUE)
public ResponseBodyEmitter streamLogs() {
ResponseBodyEmitter emitter = new ResponseBodyEmitter();
new Thread(() -> {
try {
while (true) {
String logEntry = getLatestLogEntry();
if (logEntry != null) {
emitter.send(logEntry);
}
Thread.sleep(1000);
}
} catch (Exception e) {
emitter.completeWithError(e);
}
}).start();
return emitter;
}
private String getLatestLogEntry() {
// Simulated log entry
return "2025-02-12 12:00:00 - INFO: User logged in successfully.";
}
}Core Methods
send(Object data) : Sends a piece of data to the client; can be called multiple times.
complete() : Marks the response as finished.
onTimeout(Runnable callback) : Sets a timeout callback.
onCompletion(Runnable callback) : Sets a completion callback.
How It Works
ResponseBodyEmitter uses HTTP chunked transfer encoding, allowing the server to push data blocks as soon as they are ready without specifying a Content‑Length. The client can process each chunk immediately, reducing latency.
Connection lifecycle is managed by calling complete() when all data is sent or completeWithError() on failure, preventing resource leaks.
Comparison with Other Streaming Techniques
Streaming (OutputStream) : Gives full control but requires manual stream management.
Server‑Sent Events (SSE) : Uses the text/event‑stream protocol and needs client support.
ResponseBodyEmitter : Works with any HTTP client, integrates easily with Spring, and is suitable for AI‑style incremental responses.
Best Practices and Caveats
Ensure client compatibility with chunked responses.
Configure a timeout to avoid indefinitely open connections, e.g., emitter.onTimeout(() -> emitter.complete()); .
Although send() is thread‑safe, manage thread lifecycles to avoid leaks.
Always close the connection with complete() or completeWithError() after processing.
Conclusion
ResponseBodyEmitter provides a lightweight, Spring‑native solution for real‑time, high‑concurrency data delivery, enabling features such as progress bars, live chat, stock tickers, and log streaming.
Architecture Digest
Focusing on Java backend development, covering application architecture from top-tier internet companies (high availability, high performance, high stability), big data, machine learning, Java architecture, and other popular fields.
How this landed with the community
Was this worth your time?
0 Comments
Thoughtful readers leave field notes, pushback, and hard-won operational detail here.