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 连接。
这段代码的核心部分是:
req.on('close', () => { clearInterval(intervalId); res.end(); });
在这个回调函数中,首先使用 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