Server-sent Events: 如何优化事件驱动流式传输?

简介

Server-sent Events (SSE) 是一种基于 HTTP 的单向数据流协议,它通过浏览器与服务器之间的长时连接,实现了服务端实时向客户端推送数据的能力。不同于 WebSocket,SSE 仅支持服务器向客户端的单向数据传输,不支持客户端向服务器发送消息。

SSE 可以用于实现许多实时应用场景,如实时聊天、股票行情、运动比赛等。本文将探讨 SSE 的优化方法,让我们的 SSE 式应用更加健壮、稳定。

SSE 的特点

SSE 的主要特点如下:

  • 基于 HTTP,支持跨域传输。
  • 单向数据传输,仅支持服务器向客户端发送数据。
  • 支持自动重连,当连接中断时自动尝试重新连接。
  • 支持事件流式传输,并在传输过程中可以动态更新事件名、事件 ID 等元数据信息。

SSE 的工作原理

与 WebSocket 不同,SSE 是基于 HTTP 长连接实现数据的传输的。连接的建立也与普通的 HTTP 连接类似,客户端通过创建一个新的 EventSource 对象来建立与服务端的连接:

----- ----------- - --- -----------------------

其中,/stream 是服务端提供的 SSE 接口。

服务端通过设置 HTTP 响应头,告知浏览器这是一条 SSE 数据流。下面是一个示例的响应头:

------------- -----------------
-------------- --------
----------- ----------

其中,Content-Type 告知浏览器响应内容类型,Cache-Control 禁用浏览器缓存,Connection 保持连接不关闭。

服务端发送的数据通过一些特殊的格式进行传输,格式如下:

------ ------
--- --- ---
----- ------

其中,"event" 和 "id" 是可选的元数据信息,可以不设置。"data" 则是事件的主体内容,每行仅能包含一条事件。

当服务端发送数据时,浏览器会触发一个 message 事件,我们可以通过监听这个事件来处理服务端发送的事件数据:

--------------------------------------- --------------- -
  ----- ---- - -----------------------
  ------------------
---

SSE 的优化方法

重试机制

由于 SSE 是建立在 HTTP 长连接之上,所以连接在传输过程中可能随时中断。这时,浏览器会自动尝试重新连接。但是这个重试间隔时间可能比较长,这对实时应用场景来说是不可接受的。

我们可以通过在服务端设置 Retry-Time 来控制浏览器进行重连的时间间隔,如下:

------ ------

重试时间以毫秒为单位。在服务端发送 Retry-Time 头字段后,如果连接断开,浏览器将按照这个时间间隔自动尝试重新连接。

丢失事件恢复

在传输数据流的过程中,如果某条事件的数据传输失败,它不会触发 message 事件,这样就会导致事件的丢失。对于对数据实时性要求高的应用场景来说,这是不可接受的。

我们需要一种机制来保证事件传输的可靠性。以 Vue.js 为例,在 data 接口中提供了一个 Last-Event-ID 的参数,用于标识接收到的最后一个事件 ID。服务端可以通过 Last-Event-ID 这个参数来保证事件序列不被破坏。

客户端发送 SSE 请求时,可以通过设置 withCredentials 参数为 true 来使跨域请求可以携带 cookie 等身份信息:

----- ----------- - --- ---------------------- - ---------------- ---- ---

在服务端响应数据时,需要加入 Last-Event-ID 参数:

--- --- -----

在客户端接收数据时,需要先根据 Last-Event-ID 进行事件序列的补偿,保证事件不丢失:

--- ----------- - ----------------------------------- -- -----

--------------------- - -------- ------- -
  ----- ---- - -----------
  ----- ------- - ------------------------------
  
  -- -- ----------- ------- -----------------------
  -- ------------- -- ------------ --- --------- -
    ----------- - --------
    ----------------------------------- ---------
    -- ------- -- -
  - ---- -
    -- ---------------
    ---------------------------------------
    --------------------
    ----------
  -
--

在每次传输完成后,客户端需要将最后一个事件 ID 存储到本地,以便下一次传输时进行补偿。

事件标识机制

当我们在实现 SSE 应用时,可能会遇到需求动态添加、删除事件监听函数的情况。如果在仅通过事件名称匹配时,没有办法精准地定位到某个特定事件所对应的所有监听函数。

我们可以通过动态更新事件的元数据信息来对每个事件进行标识。例如,我们可以通过修改事件名及其对应的 ID 来标识事件。对于需要动态添加、删除监听函数的场景,我们只需根据事件名称及其 ID 进行监听即可。

资源优化

由于 SSE 是基于 HTTP 长连接实现的,因此它是非常耗费资源的。为了减少服务器负载及网络带宽的消耗,我们可以通过控制每次传输事件的大小、事件的传输频率等方法来减小 SSE 请求的资源消耗。

总结

本文介绍了 SSE 的特点及工作原理,并针对 SSE 应用中的一些常见问题,提出了相应的优化措施。通过这些优化方法,可以使 SSE 在实际应用中更为稳定、高效。

来源:JavaScript中文网 ,转载请注明来源 本文地址:https://www.javascriptcn.com/post/6649ae44d3423812e4896a63