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

相关推荐
淘矿人1 天前
Claude辅助DevOps实践
java·大数据·运维·人工智能·算法·bug·devops
星浩AI1 天前
OpenHuman 对比 OpenClaw、Hermes Agent
人工智能·后端·agent
小江的记录本1 天前
【Java基础】泛型:泛型擦除、通配符、上下界限定(附《思维导图》+《面试高频考点清单》)
java·数据结构·后端·mysql·spring·面试·职场和发展
来恩10031 天前
请求转发与响应重定向的使用
java
@杰克成1 天前
Java学习30
java·开发语言·学习
次元工程师!1 天前
LangFlow开发(三)—Bundles组件架构设计(3W+字详细讲解)
java·前端·python·低代码·langflow
oddsand11 天前
Redis网络模型
java·数据库·redis
皮卡祺q1 天前
【redies0-导论】分布式系统的演进-引进redis原因
java·数据库·redis
roman_日积跬步-终至千里1 天前
如何分析复杂架构:一套真正能落地的方法
java·开发语言·架构
geovindu1 天前
go: Semaphore Pattern
开发语言·后端·设计模式·golang·企业级信号量模式