Spring 实现 3 种异步流式接口,干掉接口超时烦恼

很多人第一次遇到「接口执行很久、前端一直转圈、最后网关直接超时」这种问题时,通常的第一反应都是:加线程池、加缓存、改 SQL。但有些场景本质不是慢,而是需要持续输出,比如:

  • 文件实时生成并回传
  • AI 大模型流式回答
  • 长时间任务进度推送
  • 日志/监控连续输出

这类需求要是硬做成普通 HTTP 同步接口,超时是迟早的事。 好消息是:Spring 自带几套流式输出方式,能让接口不断推送内容,不占线程、不怕长耗时。

下面我用一个常见场景来举例:\

模拟一个"持续生成数据并实时返回给前端"的接口,三种方式逐个实现。

① 使用 ResponseBodyEmitter:最轻量、最好上手、兼容性好

如果你只是想让接口边算边返回内容,ResponseBodyEmitter 是最简单的。

它的特点是:

  • 支持分段写出
  • 不阻塞 servlet 容器线程
  • 前端收到的内容会一段段实时流入

代码示例:

bash 复制代码
@GetMapping("/stream/emitter")
public ResponseBodyEmitter emitter() {
    ResponseBodyEmitter emitter = new ResponseBodyEmitter();

    Executors.newSingleThreadExecutor().submit(() -> {
        try {
            for (int i = 1; i <= 5; i++) {
                emitter.send("chunk-" + i + "\n");
                Thread.sleep(1000);
            }
            emitter.complete();
        } catch (Exception e) {
            emitter.completeWithError(e);
        }
    });

    return emitter;
}

拿这个接口用 curl 测试,你会看到每秒打印一条,不会等五秒后一次性返回。

适用场景

  • 轻量级数据流
  • 需要简单分片输出
  • 内网 API、工具接口

缺点

  • 大量并发下对容器线程还是有压力
  • 控制能力不如 SSE

② Server-Sent Events(SSE):浏览器天然支持、适合实时推送

SSE 是浏览器原生支持的"后端到前端的单向实时通道",比 WebSocket 简单多了。SpringBoot 直接用 SseEmitter 就能输出 SSE 标准格式。

示例代码

bash 复制代码
@GetMapping("/stream/sse")
public SseEmitter sse() {
    SseEmitter emitter = new SseEmitter(0L); // 不超时

    Executors.newSingleThreadExecutor().submit(() -> {
        try {
            for (int i = 1; i <= 5; i++) {
                emitter.send(SseEmitter.event()
                        .id(String.valueOf(i))
                        .data("msg-" + i));
                Thread.sleep(1000);
            }
            emitter.complete();
        } catch (Exception e) {
            emitter.completeWithError(e);
        }
    });

    return emitter;
}

前端只需要这样接收:

bash 复制代码
const es = new EventSource('/stream/sse');
es.onmessage = e => console.log(e.data);

优点

  • 浏览器天然支持
  • 自动断线重连
  • 语义明确、协议轻量

缺点

  • 单向(只能后端→前端)
  • 对网关(如 nginx)需要配置禁用缓冲

③ Spring WebFlux:真正的异步、非阻塞、适合高并发

如果你需要同时满足:

  • 高并发大量
  • 长连接持续
  • 流式输出

那 WebFlux 才是最稳的解决方案。

使用 Flux 就能天然实现流输出:

bash 复制代码
@GetMapping(value = "/stream/flux", produces = MediaType.TEXT_EVENT_STREAM_VALUE)
public Flux<String> fluxStream() {
    return Flux.interval(Duration.ofSeconds(1))
               .map(i -> "msg-" + i);
}

特点就是:

  • Reactor 驱动,完全异步非阻塞
  • 连接数大量增长也能扛
  • 天生适合流式接口

适用场景

  • AI 对话类接口
  • 海量实时消息订阅
  • 高频监控推送

缺点

  • 需要上手 WebFlux 编程模型
  • 与传统 Spring MVC 混用时需明确路由边界

到底什么时候用哪种?

可以简单这样选:

需求 推荐方式
小量流式输出 ResponseBodyEmitter
前端需要实时显示、中轻度并发 SSE(SseEmitter)
海量长连接、AI/日志推流 WebFlux(Flux)

实战经验:避免被网关干掉

很多人在本地测试都好好的,上线后却发现还是超时。 通常不是 Spring 的问题,而是网关缓冲机制导致。

如果你用 SSE 或分段输出,需要在 nginx 加:

bash 复制代码
proxy_buffering off;
proxy_cache off;

否则 nginx 会把你辛辛苦苦的流式内容全部吞掉,凑够缓冲后一次性返回,你的"流"就没了。

"接口超时"有时候不是接口慢,而是它本来就不是"同步"该干的事情。 Spring 给了我们三种流式返回方式,只要用对,不但不卡、不断连,还能在体验上秒杀常规接口。

相关推荐
点光11 小时前
使用Sentinel作为Spring Boot应用限流组件
后端
不要秃头啊12 小时前
别再谈提效了:AI 时代的开发范式本质变了
前端·后端·程序员
有志13 小时前
Java 项目添加慢 SQL 查询工具实践
后端
山佳的山13 小时前
KingbaseES 共享锁(SHARE)与排他锁(EXCLUSIVE)详解及测试复现
后端
Leo89913 小时前
rust 从零单排 之 一战到底
后端
程序员清风14 小时前
程序员兼职必看:靠谱软件外包平台挑选指南与避坑清单!
java·后端·面试
鱼人14 小时前
MySQL 实战入门:从“增删改查”到“高效查询”的核心指南
后端
大鹏198814 小时前
告别 Session:Spring Boot 实现 JWT 无状态登录认证全攻略
后端
Java编程爱好者14 小时前
从 AQS 到 ReentrantLock:搞懂同步队列与条件队列,这一篇就够了
后端
鱼人14 小时前
Nginx 全能指南:从反向代理到负载均衡,一篇打通任督二脉
后端