java实现流式输出

在 Java 中实现 流式输出(Streaming Output),通常指:
逐块(chunk by chunk)生成并返回响应内容,而不是等全部结果生成完再一次性返回。这在大模型对话、日志推送、文件下载等场景非常常见。

下面以 Spring Boot + SseEmitter 为例,展示如何实现真正的 HTTP 流式输出

什么是 SseEmitter

  • 它是 Spring 提供的一个类,用于支持 **Server-Sent Events (SSE)**。
  • SSE 是一种 服务器向浏览器单向推送文本数据 的 HTTP 长连接技术。
  • 浏览器通过 EventSource API 接收,天然支持自动重连。
  • 内容类型为:text/event-stream

完整实现

  1. controller层

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    @RestController
    public class AiStreamController {

    @Autowired
    private AiService aiService;

    @GetMapping("/ai/stream")
    public SseEmitter streamAiResponse(@RequestParam String prompt) {
    SseEmitter emitter = new SseEmitter(60_000L); // 超时时间:60秒

    // 异步处理,避免阻塞 Tomcat 线程
    CompletableFuture.runAsync(() -> {
    try {
    // 调用你的流式生成逻辑
    aiService.streamGenerate(prompt,
    token -> {
    try {
    // 发送一个 token(自动 flush)
    emitter.send(token);
    } catch (IOException e) {
    // 客户端断开连接时会抛异常,可忽略或记录
    emitter.complete();
    }
    },
    () -> {
    // 流结束
    emitter.complete();
    }
    );
    } catch (Exception e) {
    emitter.completeWithError(e);
    }
    });
    return emitter;
    }
    }
  2. service层

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    @Service
    public class AiService {

    // 模拟或真实调用大模型流式接口
    public void streamGenerate(String prompt, Consumer<String> tokenConsumer, Runnable onComplete) {
    // 示例:模拟流式输出
    String[] tokens = {"你", "好", ",", "我", "是", "AI", "助", "手", "。"};

    for (String token : tokens) {
    tokenConsumer.accept(token);
    try {
    Thread.sleep(200); // 模拟网络延迟
    } catch (InterruptedException e) {
    Thread.currentThread().interrupt();
    return;
    }
    }
    onComplete.run();
    }
    }