解决记得调用 res.end() 以关闭 EventSource 的 Server-Sent Events 问题

阅读时长 5 分钟读完

Server-Sent Events (简称 SSE) 是一种前端实现服务器推送的技术。借助 SSE,服务器可以主动向客户端推送数据,而不需要客户端发起请求。SSE 使用 EventSource API 在浏览器中实现。但是在使用 SSE 过程中,可能会遇到 res.end()未及时调用 导致 SSE 无法正常关闭的问题,本文将详细介绍这一问题的产生原因,并给出解决方案。

1. 问题原因

在使用 SSE 实现服务器推送消息时,常常是在发送完数据后即可关闭 SSE。下面是一个简单的发送事件流的代码示例:

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

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

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

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

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

上面的代码简单地创建了一个 HTTP 服务器,并监听端口为 3000。在处理客户端请求时,服务器代码设置了 SSE 响应头,同时每秒向客户端发送当前时间的字符串,以达到每秒向客户端推送消息的目的。但是,我们可以发现,在代码中调用了 res.write() 方法,但并没有调用 res.end() 方法来关闭 SSE。

当我们在浏览器中访问服务器地址 http://localhost:3000 时,可以发现 SSE 正常地向客户端推送消息,但当我们再次访问 http://localhost:3000 时,可以观察到消息推送依然在继续,而这将导致服务器资源被浪费,影响应用性能。

这是因为,当使用 SSE 时,服务器会同时打开两个长连接(即 HTTP 连接),一个用于 SSE,另外一个用于解析 HTTP 请求(可以通过 EventSource 构造函数的第二个参数 { withCredentials: true } 明显观察到)。由于 SSE 需要保持长连接,如果我们不及时调用 res.end() 方法关闭 SSE,那么客户端和服务器之间的 SSE 连接将一直保持打开状态,从而导致资源被占用。

2. 解决方案

解决本问题的方法非常简单,即:在 SSE 推送完毕之后,调用 res.end() 方法关闭 SSE 连接。

上面的代码中已经提到了在每次 SSE 推送数据前检查 res.finished 属性,直到 res.finished 被设置为 true 才停止定时任务。这里可以根据 res.finished 的值来判断是否需要调用 res.end() 方法。

下面是修改后的代码:

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

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

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

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

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

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

修改后的代码中,加入了一个 req.on('close', handler) 的监听器。当客户端主动关闭 SSE 连接时,服务器会触发 'close' 事件,并调用 handler 函数来关闭 SSE 连接。

这段代码的核心部分是:

在这个回调函数中,首先使用 clearInterval(intervalId) 来停止定时器任务。而后,通过调用 res.end() 方法来关闭 SSE 连接,并释放资源。

通过这样的处理,当客户端主动关闭 SSE 连接时,服务器就能够及时地关闭 SSE 连接,并释放相关资源,从而避免浪费资源,提高应用性能。

3. 总结

在使用 Server-Sent Events 技术时,记得调用 res.end() 以关闭 SSE 连接,从而避免资源被浪费,提高应用性能。我们可以通过监听 'close' 事件来触发服务器关闭 SSE 连接的回调函数,在回调函数中释放相关资源。

下面是上述代码的 GitHub 仓库地址,供感兴趣的读者下载学习:https://github.com/dolphin-emu/docs-cn/blob/master/articles/3-tech/frontend/1-eventsource-problem/index.js

来源:JavaScript中文网 ,转载请注明来源 https://www.javascriptcn.com/post/65af51ebadd4f0e0ff8bbc2e

纠错
反馈