如何使用 Server-Sent Events 实现可伸缩的实时新闻推送系统?

阅读时长 9 分钟读完

在现代 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-Typetext/event-stream,这是 SSE 的 MIME 类型,表示将使用 SSE 传输格式。同时我们也设置了 Cache-Controlno-cache,这样浏览器就会注意到数据的即时性。我们通过设置 Connectionkeep-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

纠错
反馈