SSE 是什么?

SSE(Server-Sent Events)是一种基于 HTTP 协议的允许服务器向客户端推送实时数据的技术,它允许服务器通过一个持久化的 HTTP 连接,持续向客户端发送事件数据,当前常用于 AI大模型的回答推送。

为什么会有 SSE?

https://html.spec.whatwg.org/multipage/server-sent-events.html

Server-Sent Events(SSE) 是 HTML5 标准(2009 年)提出的实时通信协议,旨在解决传统轮询技术的效率瓶颈。

  • 前身技术:早期的实时数据更新依赖于 轮询(Polling)长轮询(Long-Polling),但这些方案会引发高延迟和资源浪费(例如客户端每秒发送 HTTP 请求)。

  • 标准化进程: W3C 在 HTML5 规范中明确定义 SSE 协议,通过 EventSource API 实现浏览器原生支持,为服务器主动推送数据提供标准化方案。

SSE 如何实现数据推送?

严格地说,HTTP 协议无法做到服务器主动推送信息,但是,有一种变通方法,就是服务器向客户端声明,接下来要发送的是流信息(streaming),也就是说,发送的不是一次性的数据包,而是一个数据流,会连续不断地发送过来,客户端不会关闭连接,会一直等着服务器发过来的新的数据流。总结如下:

  • SSE 使用 HTTP 建立长连接(通常是 HTTP/1.1 的 Connection: keep-alive)保持 TCP 连接不断开。

  • 服务器返回 Content-Type: text/event-stream,告诉浏览器这是一个事件流。

  • 然后服务器不断通过 HTTP 响应流(stream)发送事件数据,每条事件以特定格式推送。

  • 浏览器的 EventSource API 会自动解析这些流式数据,并触发对应的事件回调。

SSE 发送数据格式说明

一般来说,SSE 发送的数据格式长这样:

id: 123\n                  # 消息唯一标识(断线重连时通过 `Last-Event-ID` 发送)
event: temperature\n       # 事件类型(默认 `message`)
data: {"value": 25.6}\n    # 数据内容(可多行)
retry: 3000\n\n            # 重试间隔(毫秒)
  • 每个字段行以\n结束,消息以\n\n结束。

  • id: 事件 ID(可选),浏览器会用它做断线重连时的 Last-Event-ID

  • event: 事件类型(可选),用于客户端区分不同事件。

  • data: 事件的主要数据内容(必须),可以有多行,每行都以 data: 开头。

  • retry: 重连时间(可选),告诉浏览器断线重连的时间(毫秒)。

只包含data字段

data: 服务器状态正常\n data: 服务器状态正常\n\n

多行数据 + 全部字段

id: 305\n event: update\n data: 数据库已更新\n data: 请检查最新记录\n retry: 5000\n\n

只包含 event + data

event: start_stream\n data: 开始数据\n\n

SSE 代码实现

服务端

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import org.springframework.http.MediaType;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.servlet.mvc.method.annotation.SseEmitter;

@RestController
public class SSEController {

    private static final String X_ACCEL_BUFFERING = "X-Accel-Buffering";
    private static final String X_ACCEL_NO_BUFFERING = "no";

    private static final String CACHE_CONTROL = "Cache-Control";
    private static final String NO_CACHE = "no-cache";

    private static final Integer BUFFER_SIZE = 0;

    @GetMapping(path = "/sse", produces = MediaType.TEXT_EVENT_STREAM_VALUE)
    public SseEmitter streamEvents(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse) {
        SseEmitter emitter = new SseEmitter();

        // 实时流
        // 禁用代理缓冲
        httpServletResponse.setHeader(X_ACCEL_BUFFERING, X_ACCEL_NO_BUFFERING);
        // 禁用客户端缓存
        httpServletResponse.setHeader(CACHE_CONTROL, NO_CACHE);
        // 禁用响应缓冲
        httpServletResponse.setBufferSize(BUFFER_SIZE);

        // 模拟异步发送事件
        new Thread(() -> {
            try {
                for (int i = 0; i < 10; i++) {
                    emitter.send("send Message " + i);
                    Thread.sleep(1000);
                }
                // 发送结束事件
                emitter.send(SseEmitter.event().name("end").data("Stream finished"));
                emitter.complete(); // 关闭连接
            } catch (Exception e) {
                emitter.completeWithError(e);
            }
        }).start();

        return emitter;
    }


}

测试结果:

注意事项

因为 SSE 是基于响应流返回数据,需要确保 HTTP 响应流不被拦截、不被提前处理或者关闭。

SSE、SOCKET对比

特性

WebSocket(Socket)

SSE(Server-Sent Events)

通信模式

双向通信(全双工)

单向通信(服务器 → 客户端)

协议

独立协议(ws/wss)

HTTP/1.1 长连接

数据格式

文本 & 二进制

仅文本(UTF-8)

连接方式

需 HTTP Upgrade 握手

直接使用 HTTP 长连接

延迟

自动重连

需手动实现

浏览器原生支持

适用场景

聊天、游戏、实时协作

实时通知、数据流推送

SSE 和 WebSocket 功能类似,都可以用来实现服务器向客户端实时推送数据的技术,怎么选择?

  • 如果只需要服务器向客户端推送简单的通知、事件更新等,而不需要进行双向通信,选择 SSE。

  • 如果需要实现实时通信,例如在线聊天、协作编辑等,选择 WebSocket。

  • 如果应用可能在一些不支持 WebSocket 的环境中运行,或者需要考虑到更广泛的浏览器兼容性,选择 SSE。

Logo

AtomGit 是由开放原子开源基金会联合 CSDN 等生态伙伴共同推出的新一代开源与人工智能协作平台。平台坚持“开放、中立、公益”的理念,把代码托管、模型共享、数据集托管、智能体开发体验和算力服务整合在一起,为开发者提供从开发、训练到部署的一站式体验。

更多推荐