37DATA

使用 SSE 流式传输代替前端轮询

一、什么是 SSE 流式传输

SSE,全称为 Server-Sent Events(服务器发送事件),是一种允许服务器主动向客户端发送事件的技术。SSE 使用 HTTP 协议进行通信,客户端只需要发送一次请求,服务器就可以一直将数据以流的形式发送给客户端,直到客户端关闭连接。

我们在与 ChatGPT 交互时,可以发现 ChatGPT 的响应总是间断完成,查看 ChatGPT 的网络传输模式,可以发现它用的就是流式传输。

二、 SSE 流式传输的优缺点

SSE 流式传输的好处主要体现在以下几个方面:

  • 实时性:SSE 允许服务器主动向客户端推送更新,因而具有较强的实时性

  • 标准化:SSE 是一项 W3C 的标准,因此有很好的浏览器兼容性

  • 网络优化:相比传统轮询方式,SSE 能够显著减少 HTTP 请求的次数,节省带宽,减少服务器负载

  • 持久连接:客户端仅需请求一次,服务器就可以持续地推送数据,直到客户端或服务器主动关闭连接

  • 处理速度快:数据在服务器发送出后,可以立即在客户端被处理,不需要等待所有数据均被接收完毕

  • 自动重连:如果由于网络问题导致连接丢失,SSE 具有自动重连机制,客户端会在断开连接后尝试重新连接服务器

然而,SSE 也存在一些限制,例如仅支持文本数据,不支持二进制数据,只允许服务器到客户端的单向通信。但在某些应用场景下(股票价格更新、大屏数据更新等),SSE 能够提供较高的实时性能和效率。

三、 SSE 技术实现

1、响应头设置

SSE 本质上是一个基于 HTTP 协议的通信技术,在使用 SSE 时,需要在服务器端设置一些特定的响应头,以告知客户端这是一个 SSE 连接,以下是这些关键的响应头:

  • Content-Type:text/event-stream,明确告知客户端这是一个 SSE 连接

  • Cache-Control: no-cache,保证客户端获取的是最新数据

  • Connection: keep-alive,保持连接不被关闭

2、服务端响应格式

SSE 的响应主要由一系列事件组成,每个事件可能由一行或多行数据组成,事件由两个换行符(\n\n)分隔,每个事件可以包含以下字段:

  • data:事件的数据。如果数据跨越多行,每行都应该以 data: 开始

  • id:事件的唯一标识符。客户端可以使用这个ID来恢复事件流

  • event:自定义事件类型。客户端可以根据不同的事件类型来执行不同的操作

  • retry:建议的重新连接时间(毫秒)。如果连接中断,客户端将等待这段时间后尝试重新连接

Image

3、客户端处理格式

客户端一般使用 EventSource API 监听 SSE 消息,但这个 API 存在一些限制,包括:

  • 无法传递请求体,必须将请求所需的所有信息编码到 URL 中,而大多数浏览器对 URL 的长度有限制

  • 无法传递自定义请求头

  • 只支持 GET 请求,无法指定其他方法

  • 如果连接中断,无法控制重试策略,浏览器会自动进行几次尝试然后停止

推荐使用 @microsoft/fetch-event-source 库,它提供了一个基于 Fetch API 的替代接口,完全兼容 Event Stream 格式,使我们能够更灵活地进行服务器发送事件的请求。

Image

4、实现效果

Image

四、 SSE 与 WebSocket 对比

特性

SSE

WebSocket

协议

基于HTTP,使用标准HTTP连接

单独的协议(ws:// 或 wss://),需要握手升级

通信方式

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

全双工通信

数据格式

文本(UTF-8编码)

文本或二进制

重连机制

浏览器自动重连

需要手动实现重连机制

服务器负载

适合较低频率的数据更新

适合高频率消息和高度交互的场景

可以发现,SSE 与 WebSocket 各有优缺点,对于需要客户端与服务端高频交互的场景,WebSocket 确实更适合,但对于只需要服务端单向数据传输的场景,SSE 确实能耗更低,且不需要客户端感知。

五、 多个 SSE 会有什么影响

使用 SSE 可以在某些情况下提供一些好处,但多个 SSE 连接也会带来以下影响:

  • 浏览器资源占用:每个 SSE 连接都将占用一个 TCP 连接,而大多数浏览器对于单个域名的并发连接数量有限制,通常在6-8个之间。因此,如果你开启过多的 SSE 连接,可能导致新的 HTTP 请求被阻塞,直到有连接释放出来。

  • 服务器资源占用:对于服务器来说,每个 SSE 连接都需要消耗一定的资源(如内存和带宽)。如果开启大量 SSE 连接,可能会使服务器负载增加,影响服务器性能。

  • 代码复杂性:管理和维护多个 SSE 连接会使代码变得更加复杂,可能需要更多的错误处理程序来应对连接中断、重连等问题。

综上,虽然多个 SSE 连接可以帮助你区分处理不同类型的服务端消息,但在开启多个连接时应控制连接数量并考虑其潜在影响。可能的话,尽量通过使用单一的 SSE 连接,并通过服务端消息中包含的类型信息来区分处理不同类型的消息。