HTML5 中的 Server-sent Events(简称 SSE)是一种服务器推送技术,允许服务器向客户端推送实时数据。相比于传统的轮询或长轮询方式,SSE 可以通过单个 HTTP 连接实现实时数据推送,从而降低了网络带宽和服务器负载,并提高了用户体验。
SSE 的工作原理是,客户端向服务器发送一个 HTTP 请求,并在请求头中设置 Accept: text/event-stream
,表示希望接收服务器推送的事件数据。服务器接收到请求后,会向客户端发送一条 text/event-stream
格式的响应数据,其中包含一个或多个事件。每个事件由一个事件名称和事件数据组成,以及可选的事件 ID 和重试时间信息。客户端通过监听 EventSource
对象的 onmessage
事件来处理收到的事件数据,如下所示:
const eventSource = new EventSource('/sse'); eventSource.onmessage = function(event) { console.log(event.data); };
客户端接收到的数据格式如下所示:
-- -------------------- ---- ------- ------ ---- --- -- -- ------ ---- ----- ---- ------ ------------ ----- ------- ------ ------ --------------- ----- ------- ------
但是,在实际应用中,服务器推送的事件数据通常会包含一些关键信息,如消息内容、事件来源、时间戳等,并且这些信息需要被持久化存储和查询。比如,一个实时聊天应用需要将每条消息保存到数据库中,并支持根据不同条件进行查询和统计。在本文中,我们将介绍如何通过 SSE 技术实现对推送消息的持久化存储与查询。
数据库设计
在将推送消息持久化存储到数据库中之前,我们需要先设计好数据库表结构。假设我们的推送消息数据包含以下字段:
id
:消息 ID,自增主键event_name
:事件名称,用于区分不同类型的事件data
:事件数据,一个 JSON 字符串timestamp
:事件时间戳,用于记录事件发生的时间source_type
:事件源类型,如消息来源的用户、设备等source_id
:事件源 ID,如用户 ID、设备 ID 等
表结构如下所示:
-- -------------------- ---- ------- ------ ----- -------------- - ---- ------- -------- --- ---- --------------- ------------ ----------- --- ---- ------- --- ------ ---- --- ----- ----------- ------- -------- --- ---- ------- ---- ------------- ----------- --- ---- ------- --- ----------- ----------- --- ---- ------- --- ------- --- ------ - ------------- ------- ----------------
服务器实现
接下来,我们需要在服务器端实现对 SSE 推送消息的持久化存储。我们可以使用 Node.js 平台的 Express 框架来搭建一个简单的 SSE 服务器。
首先,我们需要在服务器端创建一个用于存储 SSE 连接的数组 connections
,每个 SSE 连接都对应一个客户端,用一个 response
对象来保存每个连接的响应数据,用一个 timeout
对象来保存每个连接的超时计时器。当客户端成功建立 SSE 连接时,服务器将向 connections
数组中添加一个新的 SSE 连接元素,并在超时时间内没有收到客户端的心跳数据时将该连接从 connections
数组中删除。
-- -------------------- ---- ------- ----- ------- - ------------------- ----- ---- - ---------------- ----- --- - ---------- ----- ---- - ---------------- -- ----- ----- ------------------ - -- - ----- -- ------------ ----- ----------- - --- ---------------- --------------- ----- ---- -- - ----------------------------- --------------------- ------------------------------ ------------ --------------------------- -------------- ----- ---------- - - --------- ---- -------- ------------- -- - --------------------------------------------------- --- ---------------- ---------- ---------------------------------- ----- ------ -- ------------------- -- ----------------------------- ---------------- ---------- ---------------------------------- -------------- --------------- -- -- - --------------------------------- --------------------------------------------------- --- ---------------- ---------- ---------------------------------- --------- --- --- ---------------- -- -- - ---------------- ------ -- ------- -- ---- ---------- ---
接下来,我们需要在服务器端监听数据的变化,并使用 EventSource#send
方法向客户端推送新的事件数据。在推送数据前,我们需要将数据插入到数据库中,然后根据需求生成事件名称和事件数据。最后,将事件数据通过 SSE 连接的响应对象发送给客户端。
-- -------------------- ---- ------- ----- ---- - ---------------- ----- ----- - ----------------- ----- ------ - --------------------------- ----- ------- - ------------------- -- ------------ ----- ------- - ------------------- -- ----- ----- ------- - ------------------- -- ------- ----- ----------- - ----------------------- -- --- ----- ----------- - ----------------------- -- ------- ----- ---------- - ------- ---- ------------------------ ----- ---------- ------------ ---------- ------ ------------- ----- ------ - ------------------ ----- -------- ----- -------- ----- -------- --------- ------------ --------- ------------ -------- --------- --- -------------------------- ----- -- - -- ----- - ----------------------- ---------- -------- ----- ---------------- - ---- - --------------------- ---------- -------------- --------------- - --- ----- ------ - ----------------------- ---- -- - -------------- - ---- ----------------------------- -------------- --------------- -- ------------ --- ------------------- ------------ -- -- - ------------------- -- ------- -- ------------------------- --- ----- ----------------- - ------------ -- - ------ ------------ - ---- ------- ------ ------------- ---- --------- ------ --------------- -------- ------ ---------------- - -- ----- --------- - ----------- ---------- -- - ----- ------------ - ------- ---------------- ----- ----------- - ------ ---------------------------------- ------------------------------ -- - ----------------------------------------------------------- --- -- ----- ---------------------- - ------------ --------- ----- -- - ----- --------- - ------------------------------ ----- --------- - --------------- ----------------- - ------ ----- --------- - ------------ --------- ----- ----------- ----- ------- - ----------- ------------------------------- -------------------- --------------------- -------------------- ----- ----- - ------------------------ -------- ----- ------- -- - -- ----- - ----------------------- ------ -------- ----- - ---- - -------------------- -------- ---- -- --------------------- -------------------- ----------- - --- --
最后,我们可以在 SSE 服务器启动后通过某种方式对数据进行修改或添加。比如,我们可以模拟一个消息入口,接收来自用户的消息,然后将消息数据存储到数据库并推送给客户端。
setInterval(() => { const sourceType = 'user'; const sourceId = Math.floor(Math.random() * 100) + 1; const data = {content: `Hello, user ${sourceId}`, from: 'server', to: 'all'}; insertDataAndSendEvent(sourceType, sourceId, data); }, 1000);
客户端实现
在客户端,我们可以使用 EventSource
类来处理服务器推送的事件数据。但是,在 SSE 技术中,已经收到的事件数据不能被重复接收,所以我们需要记录每个事件的最后 ID,下一次连接时从该 ID 开始接收事件数据。
-- -------------------- ---- ------- ----- ----------- - --- -------------------- --- ----------- - ----- --------------------------------------- ----- -- - ----- ---- - ----------------------- ------------------------------- ------------------ ---------------- ----------------------- ----------- - ------------------ -- ------- ------------------------------------- ----- -- - ------------------ -------- ------- -- -------
同时,客户端需要定期向服务器发送心跳数据,保持 SSE 连接不断开。
setInterval(() => { eventSource.dispatchEvent(new Event('heartbeat')); }, HEARTBEAT_INTERVAL);
最后,我们需要在 SSE 连接建立时传递上一次接收事件的最后 ID,从而避免接收到旧的事件数据。
const eventSource = new EventSource('/sse', {lastEventId});
结论
通过 SSE 技术,我们可以实现对推送消息的持久化存储和查询,并通过 SSE 推送技术将实时更新的数据即时推送到客户端,提高用户体验。但是,在使用 SSE 技术时需要注意以下几点:
- SSE 技术不支持低版本浏览器,需要使用现代浏览器或 Polyfill 库来实现兼容性;
- SSE 技术很难处理大规模推送数据,需要对服务器进行调优和优化;
- SSE 技术需要与服务器端集成,需要对服务器进行编程。
完整示例代码可以在以下代码仓库中找到:https://github.com/tommyheavenly7/node-sse-mysql-example。
来源:JavaScript中文网 ,转载请注明来源 https://www.javascriptcn.com/post/674913f893696b02680d6a8d