Springboot——SseEmitter流式输出

前言

最近做AI类的开发,看到各大AI模型的输出方式都是采取的一种EventStream的方式实现。

不是通常的等接口处理完成后,一次性返回。
而是片段式的处理完成一个分片,就立马告知前端做出处理;后续处理出新的片段则再次发送给客户端。

Spring框架中就有一个类似的方式实现。SseEmitter

SseEmitter 简介

SseEmitter 是在Spring 4.2开始引入的,使用的话需要注意版本,不过Springboot 2.X 是可以玩的。

测试demo

编写一段代码,循环返回给客户端。如下所示:

package cn.xj.controller;

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.SseEmitter;

import java.io.IOException;
import java.util.concurrent.Executor;
import java.util.concurrent.Executors;
import java.util.concurrent.TimeUnit;

@RestController
@RequestMapping("/sse/mitter")
public class SseMitterController {

    @GetMapping(value = "/stream", produces = "text/event-stream")
    public SseEmitter stream() {
        // 设置默认超时时间  0L 表示无限
        // 注意:这里的单位是  ms
        SseEmitter sseEmitter = new SseEmitter(30000L);
        // SseEmitter sseEmitter = new SseEmitter();

        // 最好不要阻塞主线程
        Executors.newSingleThreadExecutor().execute(() -> {

            try {
                for (int i = 0; i < 10; i++) {
                    sseEmitter.send("这只是一个流式输出案例:" + i);
                    TimeUnit.SECONDS.sleep(1);
                }
                // 通知客户端消息发送完毕
                sseEmitter.complete();
            } catch (Exception e) {
                e.printStackTrace();
                sseEmitter.completeWithError(e);
            }
        });

        return sseEmitter;
    }
}

浏览器请求,打开控制台查看数据格式,如下所示:
在这里插入图片描述

注意点

异常一 ResponseBodyEmitter is already set complete

这种问题通常是 设置超时时间timeout太小导致的。网上很多demo中说的这个单位是秒,但实际测试来看,单位应该是毫秒 ms

参考资料

09. Springboot集成sse服务端推流

### 实现流式输出的基础概念 HTTP 流式传输允许服务器逐步向客户端发送响应数据,而不是一次性全部发送完毕。这种方式特别适合处理大文件下载、实时日志推送等场景[^1]。 ### 使用 Postman 处理 HTTP 流式输出 Postman 支持接收并展示来自服务器的流式响应数据。为了测试和调试基于流的应用程序,在设置请求时需要注意以下几点: #### 配置 POST 请求 当通过 Postman 发送请求来触发服务端返回流式数据时,建议配置如下参数: - 添加头字段 `Accept-Encoding: identity` 来禁用压缩编码,确保可以接收到原始未压缩的数据流。 ```json { "headers": { "Content-Type": "text/event-stream", "Cache-Control": "no-cache", "Connection": "keep-alive" } } ``` #### 查看流式响应 在 Postman 中执行上述配置好的请求之后,可以在“Response”标签页下查看到来自服务器的连续更新的消息体。对于 SSE (Server-Sent Events),每条消息之间会有一个换行符分隔开;而对于普通的 chunked transfer encoding,则可能看到的是不完整的 JSON 片段或其他类型的二进制数据片段。 由于浏览器环境对外部工具的支持有限,如果希望更直观地观察整个过程中的每一个事件或数据包的变化情况,推荐配合 Wireshark 这样的网络协议分析器一起使用。 #### 编写简单的 Spring Boot 控制器用于演示目的 下面是一个简化版的例子,展示了如何创建一个能够提供流式输出的服务端点: ```java @RestController public class StreamController { @GetMapping("/stream") public ResponseEntity<StreamingResponseBody> stream() throws IOException { StreamingResponseBody responseBody = outputStream -> { try(BufferedReader reader = new BufferedReader(new FileReader("path/to/your/file.txt"))) { String line; while ((line = reader.readLine()) != null && !Thread.currentThread().isInterrupted()){ outputStream.write((line + System.lineSeparator()).getBytes(StandardCharsets.UTF_8)); outputStream.flush(); Thread.sleep(100); // Simulate delay between chunks. } } catch (IOException | InterruptedException e){ throw new RuntimeException(e); } }; return ResponseEntity.ok() .contentType(MediaType.TEXT_PLAIN) .body(responseBody); } } ``` 这段代码定义了一个 RESTful API `/stream` ,它将以逐行的方式把指定文本文件的内容推送给调用方。每次读取一行后都会立即刷新输出缓冲区以保证即时性,并且引入短暂延时模拟实际应用场景下的延迟现象。
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值