什么是SSE?服务器实时推送技术原理详解

作者:新兰2026.11.04 07:05浏览量:0

简介:Server-Sent Events(SSE)是一种基于标准HTTP协议的服务器单向推送技术,可在一条长连接上持续向客户端发送事件流。本文系统讲解SSE的定义、报文格式、工作流程与断线重连机制,对比轮询和WebSocket的差异,并梳理其在LLM流式输出、实时监控等场景的应用价值与接入注意事项,帮助开发者快速理解并正确选型。

一、SSE是什么:核心定义

Server-Sent Events(SSE,服务器发送事件)是一种构建在标准HTTP协议之上的服务器到客户端单向推送技术。它允许服务器在接受一次客户端请求后保持响应连接不关闭,并在同一条连接上以流式方式持续发送结构化的事件数据;客户端只需使用浏览器原生的 EventSource 接口,即可像监听普通事件一样接收这些数据。

可以从三个视角理解这一概念:

  • 技术视角:SSE是一种基于HTTP长连接的纯文本流式协议,响应的 Content-Type 固定为 text/event-stream,数据以UTF-8编码的事件块形式逐段传输;
  • 业务视角:SSE建立了一条“服务器主动说话、客户端安静收听”的数据通道,适合一切“服务端产生、客户端消费”的实时场景;
  • 开发视角:客户端无需引入额外通信库,创建一个 EventSource 对象即可完成订阅、接收、自动重连等全部工作,接入成本远低于自研推送方案。

需要强调的是,SSE的“单向”指的是数据流方向:服务器可以随时推送,但客户端无法通过同一条连接向服务器发送业务数据(重连时携带的协议信息除外)。

二、SSE解决什么问题

传统HTTP遵循“一问一答”模式:客户端发起请求,服务器返回完整响应后连接即结束。当业务需要“服务器有新数据就立刻告知客户端”时,这一模式会出现明显短板,常见应对方式各有代价:

  1. 短轮询:客户端定时发请求询问“有没有新数据”,绝大多数请求换来“没有”的空响应,浪费带宽与服务器算力,且数据延迟受限于轮询间隔;
  2. 长轮询:服务器把请求“挂住”,有新数据才返回。降低了空转,但每次返回后必须重新建连,高频场景下连接开销依然可观;
  3. WebSocket:提供全双工通道,能力最强,但它是独立于HTTP的协议,服务端需专门支持,经过部分代理、防火墙时可能需要额外配置,重连、心跳等也要自行实现。

SSE的设计哲学是:既然大量实时场景本质上只需要“服务器→客户端”这一个方向,那就把这一件事用最标准的方式做好——普通HTTP GET请求、一条保持打开的响应连接、约定好的文本格式。由此带来两个直接收益:

  • 接入成本极低:浏览器原生支持 EventSource,服务端只需输出正确的响应头和文本格式,几乎任何能写HTTP响应的后端技术都能实现;
  • 天然融入HTTP生态:反向代理、负载均衡、防火墙、TLS加密、Cookie鉴权等既有设施对SSE一律透明,通常无需特殊配置,这也是SSE相对独立长连接方案的一大优势。

三、核心组成:事件流的报文格式

SSE协议的核心是一个极简的文本格式。服务器将响应头设置为:

  1. Content-Type: text/event-stream

并建议同时设置不缓存相关响应头,随后持续写出事件块。每个事件块由若干“字段名: 值”形式的行组成,事件与事件之间必须以一个空行(即两个换行符 \n\n)分隔——空行是客户端判定“一个事件结束”的唯一依据。

四个标准字段:

  1. data(承载内容):真正的消息体。一个事件可包含多行 data,客户端收到的最终内容是多行按换行符拼接的结果,因此传输JSON等结构化数据非常方便;
  2. event(可选):事件类型,默认为 message。客户端可为不同 event 注册不同监听器,实现一条连接上的多路分发;
  3. id(可选):事件标识。浏览器会记住最近收到的 id,断线重连时通过 Last-Event-ID 请求头带回服务器,支撑续传;
  4. retry(可选):服务器建议客户端断线后等待多少毫秒再重连。

此外,以冒号开头的行是注释行,会被客户端忽略,常被用作心跳保活。

报文示例:

  1. : ping
  2. event: message
  3. id: 1001
  4. data: {"delta": "今天天气"}
  5. event: message
  6. id: 1002
  7. data: {"delta": "不错"}
  8. event: done
  9. id: 1003
  10. data: [DONE]

四、工作原理:从建连到推送的完整流程

一次完整的SSE交互可拆解为六步:

  1. 建连:客户端执行 new EventSource("/api/stream"),浏览器自动发起HTTP GET请求,并携带 Accept: text/event-stream 请求头;
  2. 响应保持:服务器返回200,设置 Content-Type: text/event-stream 等响应头,但不结束响应,连接持续保持打开;
  3. 增量写入:每当服务端产生新数据,就按上述格式写出一个事件块,并立即将缓冲区刷新到网络(这一步很关键,否则数据可能积压在服务端缓冲区);
  4. 客户端分发:EventSource 解析事件块,把 data 内容交给 onmessage(默认 message 类型)或对应 event 类型的监听器;
  5. 异常断开:网络波动或服务端关闭导致连接中断后,浏览器会自动在 retry 指定的时间(未指定时通常约为几秒)后重新发起请求;
  6. 续传:重连请求会自动带上 Last-Event-ID 头,服务器据此从断点之后继续发送,客户端不会感知明显的数据缺口。

服务端伪代码(与具体框架无关):

  1. 处理 GET /stream:
  2. 设置 Content-Type: text/event-stream
  3. 设置不缓存
  4. id = 0
  5. while 连接保持:
  6. data = 等待并获取下一段数据
  7. id = id + 1
  8. 写出 "id: {id}\n"
  9. 写出 "data: {data}\n\n"
  10. 刷新输出缓冲

客户端示例:

  1. const es = new EventSource("/api/stream");
  2. es.onmessage = (e) => {
  3. const chunk = JSON.parse(e.data);
  4. appendToUI(chunk.delta);
  5. };
  6. es.addEventListener("done", () => es.close());
  7. es.onerror = () => {
  8. // 浏览器会自动重连,这里通常只需做界面提示
  9. };

五、断线重连与事件恢复机制

SSE把推送可靠性中最难的两件事直接内建到了协议中:自动重连由浏览器实现,开发者无需手写重试逻辑;断点续传依赖 id 与 Last-Event-ID 的配合——服务器为每个事件赋予单调递增的 id,客户端重连时把最后收到的 id 带回,服务器只需从该 id 之后继续写流即可。这使SSE在弱网环境下的表现明显优于手写轮询或裸长连接方案。当然,服务端需要正确设计 id 的语义与存储,才能保证续传既不丢事件也不重复事件。

六、典型应用场景

  1. 大模型流式输出:LLM逐token生成的速度远慢于网络传输,逐段推送生成内容可让用户“边生成边阅读”,大幅缩短首字等待,这已成为当前SSE最典型的落地场景;
  2. 实时行情与数据看板:价格、传感器读数等持续变化的数据,由服务器在变化发生时即时推送;
  3. 监控告警与日志滚动:运维大屏实时接收新事件、新告警,替代低频轮询;
  4. 任务进度通知:批量导入、转码等长任务的进度条与状态变更通知;
  5. 内容实时更新:资讯、比分、社交动态等新内容推送;
  6. 协同场景中的服务端广播:文档变更通知、在线状态同步等“服务器通知、客户端本地决策”的模式。

七、SSE与轮询、WebSocket的区别

对比维度 轮询 SSE WebSocket
通信方向 客户端拉取 服务器推送(单向) 全双工
协议基础 标准HTTP 标准HTTP 独立协议(HTTP升级握手)
连接形态 反复建立短连接 一条长连接 一条长连接
自动重连 需自行实现 浏览器内置 需自行实现
断点续传 无 id + Last-Event-ID 需自行设计
数据类型 任意 UTF-8文本 文本与二进制
基础设施兼容 好 好,纯HTTP流 部分代理需额外配置

选型建议可概括为三句话:只需要服务器到客户端的单向实时数据,优先SSE;需要客户端也高频向服务器发数据(聊天、游戏、协同白板),选WebSocket;实时性要求仅为秒级甚至更低,普通轮询足够,不必引入长连接。

八、使用注意事项

  1. 连接数与并发限制:HTTP/1.1下浏览器对同一域名的并发连接数有限(通常6个左右,因浏览器而异),页面开多个SSE连接可能相互挤占,升级到HTTP/2后多路复用可缓解;
  2. 中间层缓冲:某些反向代理或网关默认会缓冲响应,导致事件“攒够一批”才到达客户端,部署时应确认链路上所有环节对 text/event-stream 不缓冲,服务端要逐块flush;
  3. 空闲超时:负载均衡、网关通常有空闲连接超时设置,长时间无数据可能被切断,常用做法是服务器定期发送注释行(如 : ping)作为心跳;在百度智能云等云环境中部署时,同样建议结合负载均衡与网关的超时参数统一规划心跳间隔;
  4. 幂等与续传设计:依赖 Last-Event-ID 续传时,服务器必须保证按 id 恢复的语义正确,必要时对事件做去重,避免客户端重复处理;
  5. 连接规模与扩展:每个客户端占用一条长连接,连接数、内存等资源随在线人数线性增长;多实例部署时还需解决“事件产生方与持有连接的实例不在同一节点”的路由问题,业界通常借助消息队列等通用中间件分发事件;
  6. 仅支持文本:事件流必须是UTF-8文本,二进制数据需先编码(如Base64)再传输;
  7. 鉴权与安全:SSE走标准HTTP,HTTPS、Cookie鉴权等手段全部适用;但原生 EventSource 不支持自定义请求头,若使用令牌鉴权,通常通过Cookie或URL查询参数传递,后者需注意凭证泄露风险;
  8. 资源释放:客户端关闭页面或主动close后,服务器应及时感知连接断开并释放资源,避免连接泄漏。

九、总结

回到定义本身:SSE是一种基于标准HTTP、面向“服务器到客户端”单向事件流推送的轻量级实时技术。它用最小的协议复杂度换取了三样东西——流式传输的即时性、浏览器内置的自动重连与断点续传、以及与现有HTTP基础设施的天然兼容。在LLM流式输出、实时监控、进度通知等单向推送场景中,它通常比WebSocket更经济、比轮询更实时;而在需要双向高频通信的场景中,其边界也十分清晰。理解“单向流、文本事件格式、长连接”这三个关键词,就抓住了SSE的本质。