java

关注公众号 jb51net

关闭
首页 > 软件编程 > java > Spring SseEmitter服务端推送

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

作者: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服务端推送的资料请关注脚本之家其它相关文章!

您可能感兴趣的文章:
阅读全文