在现代的 Web 应用中,实时信息推送已经成为了必要的功能之一,其中最常用的技术是 WebSocket。但在某些情况下,我们可能不需要完整的 WebSocket 功能,而只需要单向或双向的实时数据传输。这时候,可以使用 Server-Sent Events(简称 SSE)来实现。
SSE 是一种只读的 HTTP 流技术,它允许服务器实时地向客户端发送事件。由于 SSE 基于 HTTP,因此不需要额外的协议或端口,可以和现有的 Web 技术完美集成。同时,它还具有 WebSocket 不具备的优势,如自动重连、浏览器兼容性良好等。
本文将介绍如何在 Node.js 和 Koa 2 中使用 SSE 实现全双工通信。
基本使用
SSE 的核心是 EventSource 对象,它是浏览器内置的 API,可以订阅服务器发送的事件。以下是一个最简单的 SSE 示例:
const source = new EventSource('/stream') source.addEventListener('message', event => { console.log(event.data) })
在这个例子中,我们创建了一个 EventSource 对象并指定了一个 URL。浏览器会通过这个 URL 建立一个 HTTP 连接,并在连接上监听服务器发送的事件。当服务器有数据到达时,浏览器会触发 message 事件,并将数据传递给事件回调函数。
我们只需要在服务器端实现一个 SSE 接口,该接口应该使用 text/event-stream 格式向客户端发送事件。每个事件都包含一个事件类型和数据字段,如下所示:
event: message data: hello world event: custom data: {"message": "hello"}
在 Node.js 中,可以使用类似下面的代码实现 SSE 接口:
-- -------------------- ---- ------- ----- ---- - --------------- ----- ------ - ----------------------- ---- -- - -- -------- --- ---------- - ----------------------------- -------------------- ------------------------------ ----------- --------------------------- ------------- -------------- -- - ----------------- ----------- ---------------- ----- --------------------------- -- ----- - ---- - -------------- - --- --------- - -- -------------------
在这个例子中,我们创建了一个 HTTP 服务器,并在 /stream 接口上实现了 SSE。我们通过设置响应头的方式告诉浏览器,该响应使用 text/event-stream 格式,并且需要保持持久连接(即 Connection 头部设置为 keep-alive)。
在 SSE 中,每个事件可以包含一个可选的事件名称(即 event 字段),可以用来过滤事件类型。在这个例子中,我们将事件名称设置为 message,并在事件数据中返回了当时的时间戳。客户端将会每秒收到一个名为 message 的事件,并显示当前时间。
Koa 2 中的实现
现在我们来看看如何在 Koa 2 中使用 SSE 实现全双工通信。首先,我们需要封装一个 SSE 中间件,用来处理 SSE 请求和响应。
-- -------------------- ---- ------- -------- ------------- -- - ------ ----- ----- ----- -- - --------- --------------- -------------------- ---------------- ----------- ------------- ------------ -- -------- - --- ---------- ---- -- -- -- ----- ------ - -------- ----- -- - ---------- -- ------- ---------------- ----------- -- ------- ----- --------- - --- ----- ----- -------- - ------- ----- -- - ----- ------- - ---------------- ----- --- ------ ------ -- ----------------- - --------------------- - - ------- - - ---- ------- ----- - --------------- ----- -- -- ------- --------- - ---------------------------------- ------- ---------- -- --- -- - ------------------------------------- - - --------------------------------------- --------- -- ------------ --------------------- -- -- - ------------------------------------------ --------- ------------------------------------- -- ----- ------ -- ---- ------ ----------------- - - -------- --------- ------- ----- - ----- ------ - -- -- ------- ------------------- ---------- -- ----- --- ---------- ------------------ ------------------------- ------ ----------------- - ------ -
在这个中间件中,我们创建了一个 Readable 流,用来产生 SSE 数据,并将其赋值给 ctx.body。在 SSE 中,每个事件都必须以一个 ID 字段开始,该字段可以用来过滤重复数据。我们使用当前时间戳作为 ID,并将其初始化发送给客户端。
接下来,我们定义了一些 SSE 相关的方法。send 方法用于向客户端发送事件,on 方法用于注册事件监听器,off 方法用于取消注册。我们使用一个全局的 EventTarget 对象来统一管理所有事件,这可以确保不同的 Koa 实例之间也可以互相通信。
在 onRequest 回调中,我们注册了一个 'close' 事件监听器,用来在请求结束时清理事件监听器和响应流。
现在,我们就可以在路由中使用这个 SSE 中间件了。
-- -------------------- ---- ------- ----- --- - --- ----- ------------- ----- ----- -- - -- --------- --- -------- - ----------------------- ------------ -------- - - -------- ----- ------ - --- -------------------- --------------------------------- ----- -- - ----- ---- - ---------------------- ------------------------- -- --------- - - ---- - ----- ------ - -- ------------------------ ------------- ----- ----- -- - -- --------- --- -------- - --------------------- ---- -- - ---------------------- --------- ------ -- - ---- - ----- ------ - -- ----------------
在这个例子中,我们定义了一个 /chat 路由,用来展示 SSE 的使用。在页面上,我们创建了一个 EventSource 对象并订阅了 custom 事件,当服务器发送 custom 事件时,页面会显示事件的数据。
在 Koa 中,我们只需要使用 sseMiddleware 中间件即可创建 SSE 接口。在 before 中间件中,我们为 /chat 路由返回了一个 HTML 页面。在 SSE 中间件和 after 中间件中,我们注册了事件监听器和发送事件的代码。当客户端连接 SSE 接口后,它会收到名为 custom 的事件,并展示服务器发送的数据。
结论
通过本文的学习,我们了解了如何使用 Server-Sent Events 在 Node.js 和 Koa 2 中实现全双工通信。SSE 是一种轻量级的实时通信技术,具有自动重连、浏览器兼容性好等优点,在某些情况下可以替代 WebSocket。
在实际项目中,我们还需要考虑安全性、性能等问题,如如何限制客户端数量、如何保护数据安全等。与 SSE 相关的资源管理和事件管理,也需要根据具体的场景进行设计和实现。
来源:JavaScript中文网 ,转载请注明来源 https://www.javascriptcn.com/post/6750fc14050cf9039c186bca