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 给了我们三种流式返回方式,只要用对,不但不卡、不断连,还能在体验上秒杀常规接口。

相关推荐
Lenyiin2 小时前
第 97 场周赛:公平的糖果交换、查找和替换模式、根据前序和后序遍历构造二叉树、子序列宽度之和
java·c++·python·leetcode·周赛·lenyiin
青梅主码2 小时前
SimilarWeb最新发布《全球电商行业报告2025》:美国、英国、日本等成熟经济体的电商市场已显现饱和迹象,访问量趋于下降
后端
李广坤2 小时前
Rust所有权、枚举和模式匹配
后端
rannn_1112 小时前
【SQL题解】力扣高频 SQL 50题|DAY2+3
数据库·后端·sql·leetcode
酸菜谭丶2 小时前
SpringBoot工程如何发布第三方Jar
spring boot·后端·jar
明天更新2 小时前
oss存储分片的简单思路
java
bybitq2 小时前
深入浅出 Go 流程控制:从循环到延迟执行
开发语言·后端·golang
chenyuhao20242 小时前
Linux系统编程:多线程互斥以及死锁问题
linux·运维·服务器·c++·后端
凌冰_2 小时前
IDEA2025 搭建Web并部署到Tomcat运行Servlet+Thymeleaf
java·servlet·tomcat