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

相关推荐
lizhongxuan32 分钟前
Agent Sandbox: Firecracker 镜像、启动与 I/O
后端
三8441 小时前
Fastjson 漏洞 · 02 · autoType 机制与 checkAutoType
java·fastjson
打工仔折腾 AI1 小时前
飞牛OS上用Docker Compose部署ExerciseDiary运动记录并配置远程访问
运维·人工智能·后端·python·docker·容器·ai agent 实战
无限码力1 小时前
小红书笔试真题 9.17 - 待发热度重排(C++/Py/Java /Js/Go)
java·小红书·小红书笔试真题·小红书技术岗笔试真题
卓怡学长2 小时前
w194基于ssm新能源汽车充电系统小程序
java·小程序·intellij-idea
小蒜学长2 小时前
基于Python的动物救助站管理系统的设计与实现(代码+数据库+LW)
数据库·后端·python·django·动物救助
JAVA面经实录9172 小时前
Java高级后端 · 全套面试通关手册(MySQL)
java·mysql·面试
小小张说故事2 小时前
Python 中 __new__ 和 __init__ 到底谁才是构造函数?一文讲透区别与 5 个坑
后端·python
光依旧2 小时前
MCP实战手记(八):从“能跑“到“能上线“——无状态MCP Server的生产落地清单
java·人工智能·spring boot·架构·ai agent·mcp