在现代 Web 应用中,对于实时的数据更新是非常重要的。传统的轮询方式并不令人满意,因为它会导致浪费宝贵的网络带宽和服务器资源。 Server-Sent Events (SSE)是一种基于 HTTP 协议的技术,它能够实现从服务端实时推送数据给客户端,而不需要客户端不停地向服务端发起请求。
SSE 技术非常简单,它只是一个单向的持久化的连接。具有以下优点:
- 支持跨域,仅基于HTTP协议
- 实时推送数据到客户端
- 支持失败重试
SSE 的基本原理
SSE 基于 HTTP 协议,使用长轮询技术,创建一个单向、持久化的连接,从服务器实时推送数据给客户端。因为这个连接是单向的,所以客户端不能通过这个连接向服务器发送任何数据。 SSE 的架构如下所示:
- 浏览器通过 HTTP 请求与服务器建立 SSE 连接。
- 服务器返回的响应头中带有 "Content-Type: text/event-stream",以指定该响应为 SSE 响应。
- 服务器发送包含数据的文本(events)通过 SSE 连接。
- 浏览器接收到这些数据,触发事件(Event)从而更新网页。
SSE 的事件流
SSE 数据通过事件流(Event stream)的方式传递。 事件流是一系列以 "\n"(换行字符)分隔的 event 字符串。 event 字符串包含事件类型和数据,如下所示:
event: message data: Hello, world!
在这个示例中,我们定义了一个名为 "message" 的事件,并将 "Hello, world!" 作为数据传输。
“event”的 字段,指定了该事件类型,而“data”字段是该事件的数据字段。当服务器发送一个事件过来时,客户端的 JavaScript 代码接收这个event字符串,并对其进行解析。
SSE 连接的建立和维持
建立 SSE 连接很简单。 客户端代码只需要像发送 AJAX 请求一样发送一个 HTTP Get 请求,并接受响应,只需设置一些特殊的响应头信息。
// 浏览器端的JS代码 const evtSource = new EventSource("/api/sse/path"); evtSource.onmessage = function(e) { console.log("message", e.data); };
浏览器端向 "/api/sse/path" 发送了一个 GET 请求,该请求将被服务器解析为 SSE 连接,然后在响应头中返回以下信息:
HTTP/1.1 200 OK Content-Type: text/event-stream Cache-Control: no-cache Connection: keep-alive
服务器就开始向该 SSE 连接发送数据。
为了保持这个 SSE 连接的存活,浏览器和服务器之间需要发送数据。 如果 SSE 连接在一段时间内未能接收到数据,它就会被浏览器视为关闭,或假死。 因此,我们需要在服务端定期发送“心跳”事件,以保持连接的存活。
SSE 的重连机制
如果 SSE 连接的数据传输被中断,客户端会自动尝试重新连接,从而保证 SSE 的长时间存活。
- SSE 与 WebSocket 的区别
WebSocket 是一种双向通信协议,而 SSE 在一种单向通信协议。
WebSocket 其实实现了一个长轮询,只是协议和头有所改变。而SSE是对长轮询的封装形式。并且WebSocket比SSE更为强大,可以扩展和升级之前的HTTP协议。换句话说,WebSocket 支持双向通信,而 SSE 仅支持单向,而且 WebSocket 比 SSE 更容易扩展和升级。
SSE 在多个客户端中的应用
在实际应用中,需要将 SSE 连接从服务端传递给多个客户端。这通常可以通过存储 SSE 连接的列表来实现。当新的客户端到达时,服务器会将 SSE 连接添加到列表中。 然后,在服务器发送 SSE 事件时,它将转发事件给所有客户端列表中的 SSE 连接。
下面是一个 Node.js 服务器示例,在这个示例中,我们使用 Node.js 的 Express 框架来处理 SSE 请求,使用 Node.js 内置的 EventEmitter 进行事件管理:
// 服务器端Node.js代码 const express = require("express"); const EventEmitter = require("events").EventEmitter; const app = express(); // 用来存储 SSE 连接的事件管理对象 const sseEvent = new EventEmitter(); // 将新连接加入到连接列表中 app.get("/api/sse", (req, res) => { res.set({ "Content-Type": "text/event-stream", "Cache-Control": "no-cache", Connection: "keep-alive", }); sseEvent.on("message", (data) => { res.write(`data: ${JSON.stringify(data)}\n\n`); }); }); // 在一个客户端上发送消息 app.post("/api/sse/message", (req, res) => { const data = req.body; sseEvent.emit("message", data); res.status(200).send("Success"); }); app.listen(8888, () => { console.log("Server started on port 8888"); });
在客户端上的 JavaScript 代码如下所示:
// 浏览器端的JS代码 const events = new EventSource("/api/sse"); events.onmessage = function(e) { console.log("message", e.data); }; // 点击按钮发送请求 var xhr = new XMLHttpRequest(); xhr.onreadystatechange = function() { if (xhr.readyState === XMLHttpRequest.DONE && xhr.status === 200) { console.log("request complete", xhr.responseText); } }; xhr.open("POST", "/api/sse/message"); xhr.setRequestHeader("Content-Type", "application/json;charset=UTF-8"); xhr.send(JSON.stringify({ message: "Hello from client!" }));
在这个示例中,当客户端发送 POST 请求到服务器时,服务器会将其转发到连接列表中的每个 SSE 连接。 SSE 连接会将数据转发给所有客户端,在这个示例中,将在浏览器中的 console.log()
中打印消息。
总结
使用 SSE 技术可以实现服务器对多个客户端实时推送数据,从而提高系统的实时性。使用 SSE 技术可能需要一些服务器端的支持,但是它可以通过简单的 JavaScript 代码轻松地集成到任何现代 Web 应用程序中。
来源:JavaScript中文网 ,转载请注明来源 本文地址:https://www.javascriptcn.com/post/6590d98aeb4cecbf2d61fd52