核心要点
本文将探讨如何使用服务器发送事件 (SSE) 使客户端能够通过 HTTP 连接接收来自服务器的自动更新。我们还将探讨其用途,并展示如何使用 Node.js 使用服务器发送事件的实际演示。
服务器发送事件的优势
Web 基于请求-响应 HTTP 消息。您的浏览器发出 URL 请求,服务器返回数据。这可能会导致浏览器对图像、CSS、JavaScript 等发出更多请求,服务器做出响应。服务器无法主动向浏览器发送消息,那么它如何指示数据已更改?幸运的是,您可以使用服务器发送事件 (SSE) 添加诸如实时新闻快报、天气预报和股票价格等功能。
使用标准 Web 技术实现实时数据更新一直是可能的:
这两个选项都不是理想的,因为浏览器必须触发刷新。如果它过于频繁地发出请求,则不会有任何数据更改,因此浏览器和服务器都会执行不必要的工作。如果它请求太慢,它可能会错过重要的更新,而您正在关注的股票价格已经暴跌!
服务器发送事件 (SSE) 允许服务器随时向浏览器推送数据:
本质上,SSE 是一个无限的数据流。可以将其视为下载一个无限大的文件,该文件以您可以拦截和读取的小块的形式下载。
SSE 最初于 2006 年实施,所有主要浏览器都支持该标准。它可能不如 WebSockets 广为人知,但服务器发送事件更简单,使用标准 HTTP,支持单向通信,并提供自动重新连接。本教程提供了无需第三方模块的示例 Node.js 代码,但 SSE 可用于其他服务器端语言,包括 PHP。
服务器发送事件快速入门
以下演示实现了一个 Node.js Web 服务器,该服务器以至少每三秒一次的随机间隔输出 1 到 1000 之间的随机数。
您可以在这里找到我们的 Node.js SSE 演示。
该代码使用标准 Node.js http 和 url 模块来创建 Web 服务器和解析 URL:
<code class="language-javascript">import http from "node:http"; import url from "node:url";</code>
服务器检查传入的 URL 请求,并在遇到 /random 路径时做出反应:
<code class="language-javascript">const port = 8000; http.createServer(async (req, res) => { // 获取 URI 路径 const uri = url.parse(req.url).pathname; // 返回响应 switch (uri) { case "/random": sseStart(res); sseRandom(res); break; } }).listen(port); console.log(`server running: http://localhost:${port}\n\n`);</code>
它最初会使用 SSE HTTP 事件流标头进行响应:
<code class="language-javascript">// SSE 头 function sseStart(res) { res.writeHead(200, { Content-Type: "text/event-stream", Cache-Control: "no-cache", Connection: "keep-alive" }); }</code>
另一个函数随后发送一个随机数,并在随机间隔过去后调用自身:
<code class="language-javascript">// SSE 随机数 function sseRandom(res) { res.write("data: " + (Math.floor(Math.random() * 1000) + 1) + "\n\n"); setTimeout(() => sseRandom(res), Math.random() * 3000); }</code>
如果您在本地运行代码,则可以使用终端中的 cURL 测试响应:
<code class="language-bash">$> curl -H Accept:text/event-stream http://localhost:8000/random data: 481 data: 127 data: 975</code>
按 Ctrl | Cmd 和 C 终止请求。
浏览器的客户端 JavaScript 使用 EventSource 对象构造函数连接到 /random URI:
<code class="language-javascript">// 客户端 JS const source = new EventSource("/random");</code>
传入的数据会触发消息事件处理程序,其中 data: 后面的字符串在事件对象的 .data 属性中可用:
<code class="language-javascript">source.addEventListener('message', e => { console.log('RECEIVED', e.data); });</code>
{ withCredentials: true }
参数以发送 Cookie。高级服务器发送事件
SSE 不需要比上面显示的更多代码,但以下部分将讨论其他选项。
服务器可以提供任意数量的 SSE 通道 URL。例如:
如果单个页面显示一个主题,这可能是实用的,但如果单个页面显示新闻、天气和股票价格,则并非如此。在这种情况下,服务器必须为每个用户维护三个连接,这可能会随着流量的增加而导致内存问题。
另一种选择是提供单个端点 URL,例如 /latest,它在一个通信通道上发送任何数据类型。浏览器可以在 URL 查询字符串中指示感兴趣的主题,例如 /latest?type=news,weather,stockprice,以便服务器可以将 SSE 响应限制为特定消息。
来自服务器的消息可以具有关联的 event:
,它在 data:
行上方传递,以识别特定类型的信息:
<code class="language-javascript">import http from "node:http"; import url from "node:url";</code>
这些不会触发客户端的“message”事件处理程序。您必须为每种类型的事件添加处理程序。例如:
<code class="language-javascript">const port = 8000; http.createServer(async (req, res) => { // 获取 URI 路径 const uri = url.parse(req.url).pathname; // 返回响应 switch (uri) { case "/random": sseStart(res); sseRandom(res); break; } }).listen(port); console.log(`server running: http://localhost:${port}\n\n`);</code>
服务器还可以选择在 data:
行之后发送 id:
:
<code class="language-javascript">// SSE 头 function sseStart(res) { res.writeHead(200, { Content-Type: "text/event-stream", Cache-Control: "no-cache", Connection: "keep-alive" }); }</code>
如果连接断开,浏览器会在 Last-Event-ID HTTP 标头中将最后一个 ID 发送回服务器,以便服务器可以重新发送任何错过的消息。
最新的 ID 也可在客户端的事件对象的 .lastEventId 属性中获得:
<code class="language-javascript">// SSE 随机数 function sseRandom(res) { res.write("data: " + (Math.floor(Math.random() * 1000) + 1) + "\n\n"); setTimeout(() => sseRandom(res), Math.random() * 3000); }</code>
尽管重新连接是自动的,但您的服务器可能知道在特定时间段内不需要新数据,因此无需保留活动的通信通道。服务器可以在其自身或作为最终消息的一部分发送 retry:
响应,其中包含毫秒值。例如:
<code class="language-bash">$> curl -H Accept:text/event-stream http://localhost:8000/random data: 481 data: 127 data: 975</code>
收到后,浏览器将放弃 SSE 连接,并在延迟时间过去后尝试重新连接。
除了“message”和命名事件之外,您还可以在客户端 JavaScript 中创建“open”和“error”处理程序。
当服务器连接建立时,会触发“open”事件。它可用于运行其他配置代码或初始化 DOM 元素:
<code class="language-javascript">// 客户端 JS const source = new EventSource("/random");</code>
当服务器连接失败或终止时,会触发“error”事件。您可以检查事件对象的 .eventPhase 属性以查看发生了什么:
<code class="language-javascript">source.addEventListener('message', e => { console.log('RECEIVED', e.data); });</code>
请记住,无需重新连接:它会自动发生。
浏览器可以使用 EventSource 对象的 .close() 方法终止 SSE 通信。例如:
<code>event: news data: SSE is great! event: weather data: { "temperature": "20C", "wind": "10Kph", "rain": "25%" } event: stock data: { "symbol": "AC", "company": "Acme Corp", "price": 123.45, "increase": -1.1 }</code>
服务器可以通过以下方式终止连接:
retry:
延迟,然后只有浏览器可以通过创建新的 EventSource 对象来重新建立连接。
结论
服务器端事件提供了一种实现实时页面更新的方法,这种方法可能比基于 Fetch() 的 Ajax 轮询更容易、更实用且更轻量级。复杂性在于服务器端。您必须:
但这完全在您的控制之下,并且扩展应该不比任何其他 Web 应用程序更复杂。
唯一的缺点是 SSE 不允许您从浏览器向服务器发送消息(除了初始连接请求)。您可以使用 Ajax,但这对于动作游戏等应用程序来说太慢了。对于适当的双向通信,您需要 WebSockets。请查看如何在 Node.js 中使用 WebSockets 创建实时应用程序以了解更多信息!
以上是如何在node.js中使用服务器范围的事件的详细内容。更多信息请关注PHP中文网其他相关文章!