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 往往是最优选择。

相关推荐
郑州光合科技余经理3 小时前
同城系统海外版:一站式多语种O2O系统源码
java·开发语言·git·mysql·uni-app·go·phpstorm
一只乔哇噻3 小时前
java后端工程师+AI大模型开发进修ing(研一版‖day60)
java·开发语言·人工智能·学习·语言模型
Dolphin_Home3 小时前
笔记:SpringBoot静态类调用Bean的2种方案(小白友好版)
java·spring boot·笔记
喵个咪4 小时前
初学者入门:用 go-kratos-admin + protoc-gen-typescript-http 快速搭建企业级 Admin 系统
后端·typescript·go
MetaverseMan4 小时前
Java虚拟线程实战
java
刘一说4 小时前
Nacos 权限控制详解:从开源版 v2.2+ 到企业级安全实践
spring boot·安全·spring cloud·微服务·nacos·架构·开源
浪潮IT馆4 小时前
Tomcat运行war包的问题分析与解决步骤
java·tomcat
悟能不能悟5 小时前
Caused by: java.sql.SQLException: ORA-28000: the account is locked怎么处理
java·开发语言
Q_Q5110082855 小时前
python+django/flask+vue的大健康养老公寓管理系统
spring boot·python·django·flask·node.js