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

相关推荐
这个昵称不容易20 小时前
SpringBoot「衣依」服装销售平台:144个Java文件180个接口,从挑衣服到收货评价整条链路都在
java·springboot·毕设
计算机毕设定制辅导-无忧学长21 小时前
《基于微信小程序的居家医疗陪护预约系统设计与实现》
java·spring boot·微信小程序·uniapp·居家医疗陪护预约系统
文丑颜不良啊1 天前
LeetCode2221~2240题目解析
java·leetcode
秋饼1 天前
Computer Use Agent Java 落地实战:感知-行动循环、执行器与安全围栏生产级实现
java·ai·技术分享·后端开发
wo_jack1 天前
C++ 入门笔记(十六):第16课 vector 模拟实现十问十答 —— 从三指针到迭代器失效
c++·后端·面试·stl·vector
量化分析码农1 天前
【Python量化波动率实战 02】波动率会传染:EWMA 指数加权给近期行情更大话语权
后端
文丑颜不良啊1 天前
LeetCode2161~2180题目解析
java·leetcode
GISer_Jing1 天前
如何学习Agent
后端·langchain
JWASX1 天前
【Go】map 源码解析 - group
后端·golang·map·源码分析
文丑颜不良啊1 天前
LeetCode2141~2160题目解析
java·leetcode