Server-Sent Events (SSE)

Posted by lvwa on April 14, 2025

本文参考 High Performance Browser Networking,获取更多详细内容请查看原文。

解析高效的实时数据传输

当我们讨论浏览器网络性能时,服务器发送事件(SSE)无疑是一个重要的组成部分。该机制使得文本形式的数据能够高效地从服务器流向客户端,适用于诸如实时通知和更新等场景。SSE主要依赖两个核心构件:一是浏览器中的EventSource接口,该接口允许客户端以DOM事件形式接收来自服务器的推送消息;二是专门用于事件传输的流数据格式,以便于传递及时的更新。

由于EventSource API与定义明确的事件流数据格式的结合,SSE已成为在浏览器环境中处理实时数据的高效工具,具备以下特征:

  • 通过持久连接进行低延迟传输
  • 高效的浏览器消息解析,无需无界缓冲
  • 自动跟踪上次接收的消息并支持自动重连
  • 以DOM事件的形式进行客户端消息通知

在底层实现上,SSE通过长生命周期的HTTP连接提供高效的XHR流(跨浏览器实现),而消息的传递过程则由浏览器管理所有的连接和消息解析,让开发者能够专注于业务逻辑的实现!总之,SSE简化了实时数据的管理。

EventSource接口详解

EventSource接口将许多低层连接建立和消息解析的事务隐藏在一个简化的浏览器API后。要使用该接口,我们需要定义SSE事件流的URL,并为事件注册适当的JavaScript事件监听器:

1
2
3
4
5
var source = new EventSource('/path/to/stream-url');
source.onopen = function() { ... };
source.onerror = function() { ... };
source.addEventListener('foo', function(event) { processFoo(event.data); });
source.onmessage = function(event) { log_message(event.id, event.data); if (event.id === 'CLOSE') { source.close(); } }

在上面的代码中,我们建立了一个新的SSE连接,配置了各种回调以处理不同的事件。具体来说:

  • 建立连接时调用的可选回调
  • 连接失败时触发的可选回调
  • 订阅特定类型的事件(如“foo”)并处理自定义逻辑
  • 处理所有未指定类型的通用事件消息
  • 如果服务器发送“CLOSE”消息ID,则终止连接

SSE还可以通过CORS(跨域资源共享)获取来自远程源的数据,充分利用XHR的相应权限和选择机制。

经过这些初步设置后,实际的逻辑实现可以转向业务逻辑本身:打开连接、处理接收到的事件并在任务完成后结束流。

事件流的通信协议

SSE事件流以流式HTTP响应的形式进行传输:客户端发起的常规HTTP请求会被服务器以一种特定的“text/event-stream”内容类型进行响应,然后流式传递UTF-8编码的数据。简单的示例如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
=> Request GET /stream HTTP/1.1
Host: example.com
Accept: text/event-stream
<= Response 200 OK
HTTP/1.1 200 OK
Connection: keep-alive
Content-Type: text/event-stream
Transfer-Encoding: chunked
retry: 15000
data: {"message": "JSON payload"}
event: foo
data: 类型为 "foo"
id: 42
event: bar
data: 多行消息:类型为 "bar" 和 id "42"
id: 43
data: 最后一条消息,id "43"

通过EventSource接口启动的连接,会产生一系列有用途的事件流,其中包含新消息的ID、类型等信息。这一协议虽然初看复杂,但其实内置了高效的消息分隔机制,允许浏览器在收到消息时迅速进行解析。

事件流协议的特性

事件流协议使得消息的有效载荷可以包含多个“data”字段的值。事件可以选择提供ID和类型字符串,这些事件边界通过换行符标记。EventSource接口在接收信息时,利用换行符解析流,提取数据内容,确认消息的ID和类型,并最终产生相应的DOM事件来通知应用程序。

除了自动解析消息,SSE还支持断开连接时的重新连接机制,通过指定重连延迟时间来优化处理过程。默认情况下,浏览器会在连接断开后,自动尝试恢复连接,通常推荐延迟2-3秒。

SSE的应用场景与性能

SSE非常适合于需要实时数据流、低延迟且消息开销小的应用场景。通过这种机制,服务器可以在消息生成的瞬间将其推送到客户端,浏览器则负责处理所有的消息解析,消除无效的无界缓冲区。同时,EventSource API自动提供重新连接功能,使得SSE成为处理实时数据的强大工具。

然而,SSE也有其局限性。它仅支持服务器到客户端的单向数据传递,不适用于大规模的上传或请求流。同时,其事件流协议专为UTF-8文本设计,对进一步的二进制流支持相对较弱。

尽管如此,UTF-8的设计限制通常可在应用层处理,SSE可以告知客户端对新二进制资源的可用性,随后客户端以XHR的方式请求数据。虽然这多了一个请求回合,但也利用了XHR的缓存和压缩特性。