当前位置: 首页 > news >正文

Spring MVC Streaming and SSE Request Processing SSE可以实现chatgpt一次请求分批次响应的效果

1. Introduction

This simple tutorial demonstrates the use of several asynchronous and streaming objects in Spring MVC 5.x.x.

Specifically, we’ll review three key classes:

  • ResponseBodyEmitter
  • SseEmitter
  • StreamingResponseBody

Also, we’ll discuss how to interact with them using a JavaScript client.

2. ResponseBodyEmitter

ResponseBodyEmitter handles async responses.

Also, it represents a parent for a number of subclasses – one of which we’ll take a closer look at below.

2.1. Server Side

It’s better to use a ResponseBodyEmitter along with its own dedicated asynchronous thread and wrapped with a ResponseEntity (which we can inject the emitter into directly):

@Controller
public class ResponseBodyEmitterController {
 
    private ExecutorService executor 
      = Executors.newCachedThreadPool();

    @GetMapping("/rbe")
    public ResponseEntity<ResponseBodyEmitter> handleRbe() {
        ResponseBodyEmitter emitter = new ResponseBodyEmitter();
        executor.execute(() -> {
            try {
                emitter.send(
                  "/rbe" + " @ " + new Date(), MediaType.TEXT_PLAIN);
                emitter.complete();
            } catch (Exception ex) {
                emitter.completeWithError(ex);
            }
        });
        return new ResponseEntity(emitter, HttpStatus.OK);
    }
}

So, in the example above, we can sidestep needing to use CompleteableFutures, more complicated asynchronous promises, or use of the @Async annotation.

Instead, we simply declare our asynchronous entity and wrap it in a new Thread provided by the ExecutorService.

2.2. Client Side

For client-side use, we can use a simple XHR method and call our API endpoints just like in a usual AJAX operation:

var xhr = function(url) {
    return new Promise(function(resolve, reject) {
        var xmhr = new XMLHttpRequest();
        //...
        xmhr.open("GET", url, true);
        xmhr.send();
       //...
    });
};

xhr('http://localhost:8080/javamvcasync/rbe')
  .then(function(success){ //... });

3. SseEmitter

SseEmitter is actually a subclass of ResponseBodyEmitter and provides additional Server-Sent Event (SSE) support out-of-the-box.

3.1. Server Side

So, let’s take a quick look at an example controller leveraging this powerful entity:

@Controller
public class SseEmitterController {
    private ExecutorService nonBlockingService = Executors
      .newCachedThreadPool();
    
    @GetMapping("/sse")
    public SseEmitter handleSse() {
         SseEmitter emitter = new SseEmitter();
         nonBlockingService.execute(() -> {
             try {
                 emitter.send("/sse" + " @ " + new Date());
                 // we could send more events
                 emitter.complete();
             } catch (Exception ex) {
                 emitter.completeWithError(ex);
             }
         });
         return emitter;
    }   
}

Pretty standard fare, but we’ll notice a few differences between this and our usual REST controller:

  • First, we return a SseEmitter
  • Also, we wrap the core response information in its own Thread
  • Finally, we send response information using emitter.send()

3.2. Client Side

Our client works a little bit differently this time since we can leverage the continuously connected Server-Sent Event Library:

var sse = new EventSource('http://localhost:8080/javamvcasync/sse');
sse.onmessage = function (evt) {
    var el = document.getElementById('sse');
    el.appendChild(document.createTextNode(evt.data));
    el.appendChild(document.createElement('br'));
};

4. StreamingResponseBody

Lastly, we can use StreamingResponseBody to write directly to an OutputStream before passing that written information back to the client using a ResponseEntity.

4.1. Server Side

@Controller
public class StreamingResponseBodyController {
 
    @GetMapping("/srb")
    public ResponseEntity<StreamingResponseBody> handleRbe() {
        StreamingResponseBody stream = out -> {
            String msg = "/srb" + " @ " + new Date();
            out.write(msg.getBytes());
        };
        return new ResponseEntity(stream, HttpStatus.OK);
    }
}

4.2. Client Side

Just like before, we’ll use a regular XHR method to access the controller above:

var xhr = function(url) {
    return new Promise(function(resolve, reject) {
        var xmhr = new XMLHttpRequest();
        //...
        xmhr.open("GET", url, true);
        xmhr.send();
        //...
    });
};

xhr('http://localhost:8080/javamvcasync/srb')
  .then(function(success){ //... });
Copy

Next, let’s take a look at some successful uses of these examples.

5. Bringing It All Together

After we’ve successfully compiled our server and run our client above (accessing the supplied index.jsp), we should see the following in our browser:


And the following in our terminal:

We can also call the endpoints directly and see them streaming responses appear in our browser.

本文通过三类对象(ResponseBodyEmitterSseEmitterStreamingResponseBody)展示了 Spring MVC 处理异步与流式响应的不同方案,涵盖服务端实现、客户端交互及效果验证,适用于实时数据推送、大文件传输等场景。

类名特点
ResponseBodyEmitter基础异步响应,需手动管理线程与数据发送。
SseEmitter支持 SSE 协议,适合服务器主动推送多次事件(如实时通知)。
StreamingResponseBody直接操作输出流,适合大文件下载或低层级流式数据处理。

相关文章:

  • 数字化转型导师坚鹏:AI大模型DEEPSEEK使用方法及案例
  • 前端知识速记--css篇:CSS3中的常见动画及实现方式
  • 二分搜索算法核心-----labuladong笔记
  • LibreOffice转换word文档
  • GC 基础入门
  • 简述 tsconfig.json 中 rootDir 和 include 之间的关系
  • 沃德校园助手系统php+uniapp
  • Windows逆向工程入门之汇编位运算
  • DeepSeek R1本地化部署:从零搭建智能对话系统
  • Vue的简单入门 一
  • MySQL数据库(八)☞ 我是不是锁神
  • 草图绘制技巧
  • 学习web数据埋点
  • 滑动窗口算法篇:连续子区间与子串问题
  • docker 基础命令使用(ubuntu)
  • 「软件设计模式」桥接模式(Bridge Pattern)
  • Vue:h渲染函数性能警告[Non-function value encountered for default slot.]
  • 4G模块非必要,不关机!关机建议先进飞行模式
  • 【linux】Socket网络编程
  • 豆瓣电影信息快速获取带api接口
  • 泽连斯基已离开土耳其安卡拉
  • 上交所五方面落实募资新规:强化关键少数责任和股东权利保障
  • 女外交官郑璇已任中国驻莫桑比克大使
  • 重庆市委原常委、政法委原书记陆克华被决定逮捕
  • 加拿大新政府宣誓就职
  • 沙县小吃中东首店在沙特首都利雅得开业,首天营业额超5万元