在现代 Web 应用程序中,实时数据的需求日益增长。传统的轮询和长轮询方法已经无法满足业务需求,因为它们惊人地浪费了带宽和服务器资源。而 Server-Sent Events(简称 SSE)因为易于使用、可伸缩性和更稳定的连接,成为了很多公司选择实现实时通信的首选。
在本文中,我将介绍 Server-Sent Events 的基础知识和如何使用 SSE 实现可伸缩的实时新闻推送系统。首先,我们将讨论什么是 SSE,然后介绍 SSE 的工作原理和如何实现一个 SSE 服务器。然后,我们将通过一个简单的新闻推送示例来演示如何在前端使用 SSE。最后,我们将探讨如何在 SSE 服务器上方实现负载均衡和扩展。
什么是 Server-Sent Events?
Server-Sent Events 是一种 HTML5 API,用于在客户端通过浏览器获取实时更新的数据。它们构建在 HTTP 上,使用纯文本(而不是二进制)的无状态流来传输数据。
SSE 的主要优点是它们异步、可靠和可伸缩。初始化 SSE 连接只需要一次 HTTP 请求,然后服务器可以向客户端发送事件数据。这可以减少网络延迟和减小带宽的使用。
另一个好处是 SSE 服务器可以通过发送专用的“保持活动”消息来确保 SSE 连接保持活动状态,这样客户端就可以随时接收数据。这使 SSE 更稳定和可靠,即使在较差的网络连接下也能正常工作。
如何使用 SSE?
SSE 服务器端的实现
要实现 SSE 服务器,我们需要添加一些特殊的 HTTP 标头,并使用 event-stream
格式给客户端发送数据。下面是一个简单 SSE 服务器示例:
-- -------------------- ---- ------- ----- ---- - ---------------- -------------------------- ----- ---- - -- -- --- -- ------------------ - --------------- -------------------- ---------------- ----------- ------------- ------------- --- -- ------ -------------------- -- - ---------------- - - --- ---------------- - -------- -- ------ ----------------
这个示例使用 http
模块创建了一个 SSE 服务器。在响应中,我们设置了 Content-Type
为 text/event-stream
,这是 SSE 的 MIME 类型,表示将使用 SSE 传输格式。同时我们也设置了 Cache-Control
为 no-cache
,这样浏览器就会注意到数据的即时性。我们通过设置 Connection
为 keep-alive
,表示需要在一个长连接上完成传输。
在服务器中,我们使用 setInterval()
每秒钟发送一个事件数据。每个事件数据包含了一个数据行以及一个两个 \n
换行符,表示一个事件的结束。
SSE 客户端的实现
在 SSE 客户端中,我们只需要使用 EventSource
对象来初始化 SSE 连接并通过 onmessage
回调处理数据的接收。下面是一个示例:
-- -------------------- ---- ------- --------- ----- ------ ------ ----- ---------------- ---------- ---------- -------- ----- ------ - --- -------------------- ---------------- - -------- ------- - ------------------------ -- --------- ------- ------ ------- -------
在 EventSource
对象的构造函数中,我们传入了 SSE 服务器地址,并为每个事件定义了一个处理函数。每当服务器发送一个新的事件数据,onmessage
回调函数会被调用,并通过 event.data
属性获取这个事件的数据。在这个示例中,我们将事件消息写入浏览器的控制台。
新闻推送示例
为了演示 SSE 的使用,我们将开发一个简单的实时新闻推送示例。该应用将定期从服务器接收新闻更新,并将新的文章显示在客户端。为了实现这个功能,我们需要编写一个 SSE 发布服务器,然后在前端创建 SSE 订阅客户端。下面是代码示例。
SSE 服务器端
-- -------------------- ---- ------- ----- ---- - ---------------- ----- -- - -------------- -------------------------- ----- ---- - -- -- --- -- ------------------ - --------------- -------------------- ---------------- ----------- ------------- ------------- --- -- ------ ----- -------- - --------- - ------------- --- ----- - -- -------------------- -- - --------------------- -------- ----- ----- - ----- ---- - ---------------------------- ----------- ------ ----------- ----- ------------------------------ ---- --- -------- -- ------ -- ------------ - ----- - -- - --- -- ------ ---------------- ---------------- -------- ---- --------
为了模拟一个实际场景,我们将从文件加载新闻数据。我们将在每次连接后,定期向客户端发布新的新闻数据。在 SSE 事件的 event:
字段中,我们定义了一个 news-update
事件名称。在 data:
字段中,我们将新闻数据作为 JSON 字符串发布。注意,在 data:
字段及其前面的行末尾必须有两个 \n
换行符才能表示事件结束。
SSE 客户端
-- -------------------- ---- ------- --------- ----- ------ ------ ----- ---------------- ----------------------- ------- ---- - ------------ ------ ----------- ---------- ----- - -------- ------- ------ ------------- --- --------------- -------- ----- ---- - -------------------------------- ----- ------ - --- -------------------- -------------------------------------- -------- ------- - ----- -- - ----------------------------- ----- ---- - ----------------------- ------------ - --- -------------------------------------- --------------------- ----------------- --- --------- ------- -------
一个客户端通过 EventSource()
函数创建一个 SSE 连接,并为事件 news-update
添加了事件处理程序来更新新闻列表。在事件回调中,所有发布的新闻数据都将用 JavaScript 对象表示,并使用 ul
列表在客户端的页面上进行展示。这个示例的结果如下图所示:
如何扩展 SSE 服务器
当收到大量的 SSE 连接时,单个 SSE 服务器可能无法处理所有连接。我们通常需要通过许多服务器将客户端连接均衡到多个 SUES 服务器上。下面是一个配置负载均衡器的示例代码:
-- -------------------- ---- ------- ----- ---- - ---------------- ----- ------- - ------------------- ----- ------- - ---------------------------- -- ------------------ - ---------------- -------------- ------- -- -- ------ -- --- ---- - - -- - - -------- ---- - --------------- - -- -- ------ ---- ------------------ -------- ----- ------- -- - ----------------- --------------------- ------ --- - ---- - -- -- --- --- -------------------------- ----- ---- - -- --- ---------------- ----------------- -------------- ------ ------- -
在这个示例中,我们使用 cluster
模块来管理多个 SSE 服务器。主进程负责创建 Worker 进程,并在子进程中创建 SSE 服务器。当其中任一子进程退出时,主进程会重新创建一个新的进程(以保持 worker 数量的恒定),以确保我们的 SSE 服务器始终可以响应客户端连接。
总结
在本文中,我们了解了 SSE 的基础知识和如何使用 SSE 实现可伸缩的实时新闻推送系统。我们在服务器和客户端上都展示了一个 SSE 示例,以及如何使用负载均衡器来弥补多个服务器的需求。
当然,还有很多其他的使用 SSE 的场景,如实时聊天应用程序、仪表盘、股票报价等。SSE 适用于任何实时通信的场景,它可以让我们的应用程序更可靠、可伸缩,并减少带宽的使用。
来源:JavaScript中文网 ,转载请注明来源 https://www.javascriptcn.com/post/64880bc248841e989468d5fc