Master Asynchronous Streaming in Spring: ResponseBodyEmitter, SseEmitter & StreamingResponseBody

This article explains how to handle time‑consuming Spring endpoints by using asynchronous streaming tools—ResponseBodyEmitter, SseEmitter, and StreamingResponseBody—detailing their use cases, code examples, and how they improve system responsiveness without blocking servlet threads.

Su San Talks Tech
Su San Talks Tech
Su San Talks Tech
Master Asynchronous Streaming in Spring: ResponseBodyEmitter, SseEmitter & StreamingResponseBody

When an interface takes a long time to process, traditional async methods like Callable, WebAsyncTask, DeferredResult or CompletableFuture can return only a single result, which is insufficient for scenarios that need continuous client updates.

Spring provides three streaming utilities— ResponseBodyEmitter, SseEmitter and StreamingResponseBody —that allow asynchronous processing while keeping the servlet thread free.

ResponseBodyEmitter

ResponseBodyEmitter

is suitable for dynamically generating content and sending it piece‑by‑piece to the client, such as file‑upload progress or real‑time logs.

Typical usage creates a ResponseBodyEmitter instance, runs a time‑consuming task asynchronously, and calls send repeatedly.

@GetMapping("/bodyEmitter")
public ResponseBodyEmitter handle() {
    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 endpoint streams a message every two seconds, mimicking the incremental response style of GPT‑like services.

SseEmitter

SseEmitter

extends ResponseBodyEmitter and is designed for Server‑Sent Events (SSE), enabling the server to push real‑time data such as notifications or status updates to the client.

Client‑side JavaScript establishes an EventSource connection and updates the page when messages arrive.

<body>
    <div id="content" style="text-align: center;">
        <h1>SSE 接收服务端事件消息数据</h1>
        <div id="message">等待连接...</div>
    </div>
    <script>
        let source = null;
        let userId = 7777;
        function setMessageInnerHTML(message) {
            const messageDiv = document.getElementById("message");
            const newParagraph = document.createElement("p");
            newParagraph.textContent = message;
            messageDiv.appendChild(newParagraph);
        }
        if (window.EventSource) {
            source = new EventSource('http://127.0.0.1:9033/subSseEmitter/' + userId);
            setMessageInnerHTML("连接用户=" + userId);
            source.addEventListener('open', function(e) { setMessageInnerHTML("建立连接。。。"); }, false);
            source.addEventListener('message', function(e) { setMessageInnerHTML(e.data); });
        } else {
            setMessageInnerHTML("你的浏览器不支持SSE");
        }
    </script>
</body>

Server‑side, a persistent map stores SseEmitter objects keyed by user ID; the emitter can later send 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);
}

Sending a request to /sendSseMsg/7777?msg=欢迎关注-->程序员小富 pushes the message to the connected browser instantly.

SSE connections automatically reconnect after a server restart.

StreamingResponseBody

StreamingResponseBody

is designed for large or continuous data streams, writing directly to an OutputStream without loading the entire payload 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.write("
".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; the same approach works for large file downloads, preventing memory overflow.

Summary

Spring’s ResponseBodyEmitter, SseEmitter and StreamingResponseBody provide straightforward ways to build asynchronous streaming APIs, greatly enhancing performance and responsiveness for long‑running or real‑time use cases.

Original Source

Signed-in readers can open the original source through BestHub's protected redirect.

Sign in to view source
Republication Notice

This article has been distilled and summarized from source material, then republished for learning and reference. If you believe it infringes your rights, please contactadmin@besthub.devand we will review it promptly.

BackendJavaspringAsynchronousResponseBodyEmitterStreamingResponseBodySseEmitter
Su San Talks Tech
Written by

Su San Talks Tech

Su San, former staff at several leading tech companies, is a top creator on Juejin and a premium creator on CSDN, and runs the free coding practice site www.susan.net.cn.

0 followers
Reader feedback

How this landed with the community

Sign in to like

Rate this article

Was this worth your time?

Sign in to rate
Discussion

0 Comments

Thoughtful readers leave field notes, pushback, and hard-won operational detail here.