使用Spring SseEmitter实现服务端推送的示例代码

 更新时间:2026年02月28日 08:41:17   作者:gs80140  
SseEmitter 是 Spring MVC 4.2+ 引入的一个类,专门用于实现 Server-Sent Events (SSE),相比于 WebSocket,SSE 更轻量(基于 HTTP 协议),且支持自动重连,所以本文给大家介绍了如何使用Spring SseEmitter实现服务端推送,需要的朋友可以参考下

SseEmitter 是 Spring MVC 4.2+ 引入的一个类,专门用于实现 Server-Sent Events (SSE)。简单来说,SSE 允许服务器在建立连接后,主动向客户端推送数据,而不需要客户端反复轮询。

相比于 WebSocket,SSE 更轻量(基于 HTTP 协议),且支持自动重连。它非常适合像实时通知、股价更新、大模型(LLM)流式输出这类单向推送场景。

核心原理解析

在传统的 HTTP 请求中,客户端发送请求,服务器返回响应,然后连接关闭。而 SseEmitter 改变了这种模式:

  1. 保持连接:服务器返回的响应头包含 Content-Type: text/event-stream
  2. 分块传输:连接保持打开状态,服务器可以多次调用 emitter.send() 发送数据包。
  3. 结束生命周期:手动调用 complete() 或因为超时/错误触发 onTimeout/onError

前言

在实时性需求日益增长的今天,我们不一定非要动用“重型武器” WebSocket。如果你只需要服务器向客户端推送消息(如:进度条更新、ChatCompletion 响应),Spring 提供的 SseEmitter 可能是你的最佳选择。

一、 核心代码实现

使用 SseEmitter 通常分为三步:创建连接、保存引用、推送消息。

1. 控制器层 (Controller)

这是建立连接的入口。

@RestController
@RequestMapping("/api/sse")
public class SseController {

    // 用于存储已连接的客户端,实际生产中建议使用专门的服务类管理
    private static final Map<String, SseEmitter> emitters = new ConcurrentHashMap<>();

    @GetMapping(value = "/subscribe/{userId}", produces = MediaType.TEXT_EVENT_STREAM_VALUE)
    public SseEmitter subscribe(@PathVariable String userId) {
        // 设置超时时间(单位毫秒),0 表示永不超时
        SseEmitter emitter = new SseEmitter(60_000L); 

        emitters.put(userId, emitter);

        // 注册回调
        emitter.onCompletion(() -> emitters.remove(userId));
        emitter.onTimeout(() -> emitters.remove(userId));
        emitter.onError((e) -> emitters.remove(userId));

        // 建立连接后立即发送一条消息,防止某些代理或浏览器认为连接已失效
        try {
            emitter.send(SseEmitter.event().name("INIT").data("Connected!"));
        } catch (IOException e) {
            emitter.completeWithError(e);
        }

        return emitter;
    }
}

2. 消息推送逻辑

你可以从任何异步线程调用 send 方法。

public void sendMessage(String userId, String content) {
    SseEmitter emitter = emitters.get(userId);
    if (emitter != null) {
        try {
            // 可以发送纯文本,也可以发送封装好的对象(会自动转 JSON)
            emitter.send(SseEmitter.event()
                    .id(UUID.randomUUID().toString())
                    .name("message") // 对应前端的事件名
                    .data(content)
                    .reconnectTime(3000)); // 提示客户端重连间隔
        } catch (IOException e) {
            emitters.remove(userId);
            emitter.completeWithError(e);
        }
    }
}

二、 前端如何接收?

浏览器内置了 EventSource API,调用非常简单:

const eventSource = new EventSource('/api/sse/subscribe/user123');

eventSource.onmessage = (event) => {
    console.log("收到通用消息:", event.data);
};

// 监听特定类型的事件 (对应后端的 .name("message"))
eventSource.addEventListener('message', (event) => {
    const data = JSON.parse(event.data);
    console.log("处理后的数据:", data);
});

eventSource.onerror = (error) => {
    console.error("SSE 错误:", error);
    eventSource.close();
};

三、 避坑指南(关键技巧)

  1. Nginx 配置:如果你使用了 Nginx 代理,必须配置 proxy_buffering off;proxy_set_header Connection "";,否则 Nginx 会缓存消息直到缓冲区满才一次性发给前端。
  2. 容器线程限制:虽然 SseEmitter 是异步的,但它依然占用一个 HTTP 连接。在高并发场景下,需要调整 Web 容器(如 Tomcat)的最大连接数。
  3. 超时处理:浏览器默认会在 SSE 断开后自动重连。在 onTimeout 中,务必调用 emitter.complete() 来清理服务器资源。
  4. 跨域问题:确保 CORS 配置允许 Last-Event-ID 等特殊请求头。

总结

SseEmitter 是 Spring 为我们封装的一把“轻巧的瑞士军刀”。它规避了 WebSocket 复杂的握手和协议转换,在处理流式输出(如 AI 对话响应)时表现极佳。

以上就是使用Spring SseEmitter实现服务端推送的示例代码的详细内容,更多关于Spring SseEmitter服务端推送的资料请关注脚本之家其它相关文章!

相关文章

  • 详解Spring Boot实战之Restful API的构建

    详解Spring Boot实战之Restful API的构建

    这篇文章主要介绍了详解Spring Boot实战之Restful API的构建,小编觉得挺不错的,现在分享给大家,也给大家做个参考。一起跟随小编过来看看吧
    2018-01-01
  • 基于springboot 配置文件context-path的坑

    基于springboot 配置文件context-path的坑

    这篇文章主要介绍了基于springboot 配置文件context-path的坑,基于很好的参考价值,希望对大家有所帮助。如有错误或未考虑完全的地方,望不吝赐教
    2022-01-01
  • MyBatis加解密插件的示例详解

    MyBatis加解密插件的示例详解

    本文介绍了使用 MyBatis 插件实现数据库字段加解密的探索过程,实际开发过程中需要注意的细节比较多,整个流程下来我对 MyBatis 的理解也加深了,对MyBatis加解密插件感兴趣的朋友跟随小编一起看看吧
    2022-08-08
  • 对Java中传值调用的理解分析

    对Java中传值调用的理解分析

    这篇文章主要介绍了对Java中传值调用的理解分析,通过分析对比,较为深入的分析了Java中传值调用的原理与用法,需要的朋友可以参考下
    2015-01-01
  • java list与数组之间的转换详细解析

    java list与数组之间的转换详细解析

    以下是对java中list与数组之间的转换进行了详细的分析介绍,需要的朋友可以过来参考下
    2013-09-09
  • Java中Json与List、Map、entity的互相转化

    Java中Json与List、Map、entity的互相转化

    在开发中,Json转换的场景往往也就是那么几个,本文主要介绍了Java中Json与List、Map、entity的互相转化,具有一定的参考价值,感兴趣的可以了解一下
    2022-07-07
  • ant使用指南详细入门教程

    ant使用指南详细入门教程

    这篇文章主要介绍了ant使用指南详细入门教程,本文详细的讲解了安装、验证安装、使用方法、使用实例、ant命令等内容,需要的朋友可以参考下
    2015-06-06
  • Spring Boot Hello World的实现代码

    Spring Boot Hello World的实现代码

    这篇文章主要介绍了Spring Boot Hello World的实现代码,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧
    2019-06-06
  • Java基础详解之包装类的装箱拆箱

    Java基础详解之包装类的装箱拆箱

    这篇文章主要介绍了Java基础详解之包装类的装箱拆箱,文中有非常详细的代码示例,对正在学习java的小伙伴们有非常好的帮助,需要的朋友可以参考下
    2021-04-04
  • SpringBoot使用Maven打包后运行失败的问题解决详解

    SpringBoot使用Maven打包后运行失败的问题解决详解

    在使用 Spring Boot 开发项目时,我们通常会使用 Maven 作为构建工具,但有时会出现运行失败的情况,本文将从多个角度分析常见错误场景,并提供详细的排查和解决方案,希望对大家有所帮助
    2025-07-07

最新评论