How to Use Spring’s Three Asynchronous Streaming APIs to Eliminate Timeout Issues
This article explains how Spring’s ResponseBodyEmitter, SseEmitter, and StreamingResponseBody can be used to build asynchronous streaming endpoints that continuously push data to clients, avoid servlet thread blocking, and prevent request‑timeout problems, with complete code examples and usage guidelines.
When a request takes a long time to process, traditional Spring async mechanisms such as Callable, WebAsyncTask, DeferredResult or CompletableFuture can only return a single result, which is insufficient for scenarios that require continuous updates to the client. Spring provides three streaming tools— ResponseBodyEmitter, SseEmitter, and StreamingResponseBody —that allow an endpoint to remain asynchronous while repeatedly sending data without blocking the servlet thread.
ResponseBodyEmitter
ResponseBodyEmitteris suitable for dynamically generating content and sending incremental updates, such as file‑upload progress or real‑time logs. The article demonstrates creating an emitter, running a simulated long‑running task, and calling send every two seconds. The emitter is constructed with a timeout value of -1L to disable timeout, and complete or completeWithError is called when the task finishes or fails.
Note: Setting the timeout to 0 or -1 disables the timeout; otherwise the connection will close after the default period.
@GetMapping("/bodyEmitter")
public ResponseBodyEmitter handle() {
// Create an emitter that never times out
ResponseBodyEmitter emitter = new ResponseBodyEmitter(-1L);
CompletableFuture.runAsync(() -> {
try {
for (int i = 0; i < 10000; i++) {
System.out.println("bodyEmitter " + i);
emitter.send("bodyEmitter " + i + " @ " + new Date() + "
");
Thread.sleep(2000);
}
emitter.complete();
} catch (Exception e) {
emitter.completeWithError(e);
}
});
return emitter;
}The demo streams a line of text every two seconds, mimicking the incremental response style of ChatGPT.
SseEmitter
SseEmitterextends ResponseBodyEmitter and implements Server‑Sent Events (SSE). It is ideal for pushing real‑time messages or status updates from server to client. The client establishes a single HTTP connection with EventSource and receives a continuous text/event-stream. The article provides both the client‑side JavaScript that creates an EventSource and the server‑side Spring controller that stores emitters in a ConcurrentHashMap and sends events.
private static final Map<String, SseEmitter> EMITTER_MAP = new ConcurrentHashMap<>();
@GetMapping("/subSseEmitter/{userId}")
public SseEmitter sseEmitter(@PathVariable String userId) {
log.info("sseEmitter: {}", userId);
SseEmitter emitterTmp = new SseEmitter(-1L);
EMITTER_MAP.put(userId, emitterTmp);
CompletableFuture.runAsync(() -> {
try {
SseEmitter.SseEventBuilder event = SseEmitter.event()
.data("sseEmitter" + userId + " @ " + LocalTime.now())
.id(String.valueOf(userId))
.name("sseEmitter");
emitterTmp.send(event);
} catch (Exception ex) {
emitterTmp.completeWithError(ex);
}
});
return emitterTmp;
}
@GetMapping("/sendSseMsg/{userId}")
public void sseEmitter(@PathVariable String userId, String msg) throws IOException {
SseEmitter sseEmitter = EMITTER_MAP.get(userId);
if (sseEmitter == null) {
return;
}
sseEmitter.send(msg);
}After starting the connection, sending a request to /sendSseMsg/7777?msg=Welcome displays the message instantly on the page. SSE also supports automatic reconnection if the server restarts.
StreamingResponseBody
StreamingResponseBodydiffers from the previous two by writing directly to the response OutputStream. It is useful for large data transfers or continuous streams, such as downloading huge files without loading the entire content into memory.
@GetMapping("/streamingResponse")
public ResponseEntity<StreamingResponseBody> handleRbe() {
StreamingResponseBody stream = out -> {
String message = "streamingResponse";
for (int i = 0; i < 1000; i++) {
try {
out.write(((message + i) + "
").getBytes());
out.flush();
TimeUnit.SECONDS.sleep(1);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
};
return ResponseEntity.ok().contentType(MediaType.TEXT_HTML).body(stream);
}The demo streams simple text lines; replacing the body with a file stream yields the same effect for large file downloads.
Conclusion
The three tools— ResponseBodyEmitter, SseEmitter, and StreamingResponseBody —are easy to use and cover a wide range of real‑time or large‑volume scenarios. By returning these objects, Spring keeps the servlet thread free, improves system responsiveness, and eliminates timeout headaches.
Demo Github address: https://github.com/chengxy-nds/Springboot-Notebook/tree/master/springboot101/%E9%80%9A%E7%94%A8%E5%8A%9F%E8%83%BD/springboot-streaming
Signed-in readers can open the original source through BestHub's protected redirect.
This article has been distilled and summarized from source material, then republished for learning and reference. If you believe it infringes your rights, please contactand we will review it promptly.
Programmer XiaoFu
xiaofucode.com – a programmer learning guide driven by the pursuit of profit
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.
