Spring 框架中的 SseEmitter 使用详解

在需要实时向前端推送数据时,很多人第一反应是 WebSocket。但如果你的需求是"服务端单向推送 ",并不需要客户端向后端发送消息,那么 Spring 提供的 SseEmitter 会更加轻量、更易用,而且天然支持断线重连。本文总结了 SseEmitter 的核心原理、常见用法、注意事项以及生产实践经验。


一、SseEmitter 是什么?

SseEmitter 是 Spring MVC 提供的一个类,用于实现 基于 HTTP 的服务器单向推送(Server-Sent Events)

它的特点包括:

  • 单向推送(服务端 → 客户端)
  • 基于标准 HTTP(不需要新协议)
  • 支持自动重连(浏览器 EventSource 自带)
  • 使用简单比 WebSocket 更轻,更适合消息通知类业务

适用场景:

  • 消息提醒
  • 实时状态更新
  • 任务进度推送
  • 监控数据刷新

二、基础用法示例

1. 创建 SSE 连接接口

java 复制代码
@GetMapping("/sse/connect")
public SseEmitter connect() {
    SseEmitter emitter = new SseEmitter(0L); // 0 表示不过期
    // 在连接时发送一条消息
    try {
        emitter.send("连接成功");
    } catch (Exception e) {
        emitter.completeWithError(e);
    }
    return emitter;
}

注意:

  • new SseEmitter(0L) 表示不超时,可减少长连接断开问题。
  • 返回后客户端会进入持续接收状态。

三、服务端主动推送消息

java 复制代码
@Autowired
private List<SseEmitter> emitters = new CopyOnWriteArrayList<>();

@GetMapping("/sse/connect")
public SseEmitter connect() {
    SseEmitter emitter = new SseEmitter(0L);
    emitters.add(emitter);

    emitter.onCompletion(() -> emitters.remove(emitter));
    emitter.onTimeout(() -> emitters.remove(emitter));

    return emitter;
}

@PostMapping("/sse/send")
public void sendMessage(@RequestParam String msg) {
    for (SseEmitter emitter : emitters) {
        try {
            emitter.send(msg);
        } catch (Exception e) {
            emitter.complete();
        }
    }
}

关键点:

  • 使用 CopyOnWriteArrayList 保存所有连接
  • 断开/异常时及时清理 emitter
  • 推送时遍历所有 emitter

四、前端(浏览器)接收实现

js 复制代码
const evtSource = new EventSource("/sse/connect");

evtSource.onmessage = function (event) {
    console.log("收到消息:", event.data);
};

浏览器自带特性:断线自动重连,不需要你自己实现。


五、常见问题与解决方案

1. 前端收不到消息?(最常见)

原因可能是:

  • 没有使用 UTF-8 文本格式
  • 推送内容太大或太快
  • 连接被代理/网关断开

解决:

java 复制代码
emitter.send(SseEmitter.event().data("内容").id("1").name("msg"));

2. Nginx 或网关 1 分钟断开

这是行业常见问题,需要配置:

复制代码
proxy_read_timeout 3600;
proxy_send_timeout 3600;

3. Spring 默认超时导致断开

务必使用:

java 复制代码
new SseEmitter(0L);

4. 消息推送异常导致连接中断

一定要在异常时清理 emitter:

java 复制代码
catch (Exception e) {
    emitter.completeWithError(e);
}

六、生产实践建议

  • 使用 心跳包 防止代理断开(比如每 20 秒发送一次空事件)
  • 使用 CopyOnWriteArrayList 或 ConcurrentHashMap 存储用户连接
  • 对每个发送操作包一层 try/catch,避免单个连接阻塞所有推送
  • 数据频繁更新建议加上消息合并策略,避免推太快导致阻塞

总结

SseEmitter 是一个轻量但功能强大的实时推送工具,特别适合实时通知类业务。相比 WebSocket,它更简单,不需要复杂协议和框架即可实现稳定的实时消息推送。

如果你的业务属于"服务端单向推送",SseEmitter 往往是最优选择。

相关推荐
a程序小傲7 小时前
蚂蚁Java面试被问:注解的工作原理及如何自定义注解
java·开发语言·python·面试
幽络源小助理7 小时前
SpringBoot+Vue摄影师分享社区源码 – Java项目免费下载 | 幽络源
java·vue.js·spring boot
0和1的舞者7 小时前
《软件测试分类指南:8 大维度 + 核心要点梳理》
java·软件测试·单元测试·测试·黑盒测试·白盒测试·测试分类
TAEHENGV7 小时前
创建目标模块 Cordova 与 OpenHarmony 混合开发实战
android·java·开发语言
无限大67 小时前
为什么"数据压缩"能减小文件大小?——从冗余数据到高效编码
后端
用户729429432237 小时前
kubernetes/k8s全栈技术讲解+企业级实战项目课程
后端
用户729429432237 小时前
基于Dubbo的分布式系统架构+事务解决方案
后端
是一个Bug7 小时前
如何阅读JDK源码?
java·开发语言
程序员鱼皮7 小时前
什么是 RESTful API?凭什么能流行 20 多年?
前端·后端·程序员
+VX:Fegn08957 小时前
计算机毕业设计|基于springboot + vue健身房管理系统(源码+数据库+文档)
数据库·vue.js·spring boot·后端·课程设计