前端开发的需求越来越多样化,其中一种常见的需求就是实时推送数据,比如聊天消息、股票行情等。为了满足这种需求,传统的轮询方式显然是不够优秀的,因为它会造成性能上的浪费。Server-sent Events(SSE)是一个可行的替代方案。
SSE 是一种基于 HTTP 的单向、持久化连接,它允许服务器发送事件数据到客户端。SSE 的工作原理大体上分为以下几个步骤:
- 客户端通过一个普通的 HTTP 请求连接到服务器。
- 服务器将建立一条持久化连接,并保持该连接处于打开状态。
- 服务器端通过该连接向客户端推送需要的数据。
SSE 的推送消息可以是一段文本,也可以是一段 JSON 字符串。常见的用法是将多个消息序列化到 JSON 中,然后通过 SSE 推送给客户端。
SSE 实现多线程并发推送的原理
SSE 推送数据的方式是通过浏览器内部实现事件流(EventSource 对象)监听服务器的数据。不过,由于 SSE 的消息只是单向通信,即服务器端可以向客户端推送消息,但反过来客户端并不能发送消息给服务器。
为了实现多线程并发推送,我们需要借助 Node.js 的事件驱动机制来解决。具体就是,每当有新的推送消息到达时,服务器会将该消息放入一个消息队列,并触发一个事件,让对应的客户端去读取该队列的消息。这就是多线程的核心实现原理。
代码示例
下面是一个使用 SSE 实现多线程并发推送的 Node.js 代码示例(假设服务器有 8 个线程):
const http = require('http'); const THREAD_NUM = 8; let messageQueue = []; let messageCount = 0; let nextClientID = 0; // 下一个客户端 ID // 创建服务器 const server = http.createServer((req, res) => { if (req.url === '/stream') { // 设置响应头 res.writeHead(200, { 'Content-Type': 'text/event-stream', 'Cache-Control': 'no-cache', 'Connection': 'keep-alive' }); // 记录当前的客户端 ID let clientID = nextClientID++; let messageID = messageCount - 1; // 发送消息到客户端 function sendEvent(event, data) { res.write(`event: ${event}\n`); res.write(`data: ${data}\n`); res.write(`id: ${++messageID}\n\n`); } // 监听客户端的请求 function onMessage(lastID) { // 如果客户端请求的消息 ID 太早,需要重新发送最新的消息 if (lastID < messageID - 10) { lastID = messageID - 10; } for (let i = lastID + 1; i <= messageCount; i++) { let message = messageQueue[i % THREAD_NUM]; if (message) { sendEvent('message', JSON.stringify(message)); } } } // 添加新的消息到消息队列 function onNewMessage(message) { messageQueue[messageCount++ % THREAD_NUM] = message; for (let i = 0; i < nextClientID; i++) { onMessage(-1); } } // 添加消息监听器 req.on('message', onNewMessage); // 发送初始消息 sendEvent('welcome', '欢迎连接到服务器'); // 监听客户端请求 let intervalID = setInterval(() => { onMessage(messageID); }, 1000); // 监听连接关闭事件 req.on('close', () => { clearInterval(intervalID); }); } else { res.writeHead(404); res.end('Not Found'); } }); // 启动服务器 server.listen(8080, () => { console.log('Server started at http://localhost:8080/'); });
在这个示例中,服务器启动时会创建一个 HTTP 服务器,客户端请求 SSE 推送数据时,服务器会为每个客户端分配一个唯一的 ID,并在每个消息上标识一个消息 ID,以便让客户端知道哪些消息是之前已经收到的,哪些是新的消息。
由于 Node.js 拥有单线程的 JavaScript 引擎,但具有事件循环机制,可以使用 Event Loop 来实现 I/O 非阻塞。这意味着,当事件发生时,Node.js 会将事件的处理推到底层实现的多线程 IO 线程池上,并通过回调函数来通知 JS 线程事件处理的进展或结果。因此,我们可以使用 Node.js 的事件驱动机制,将每个新的 SSE 消息放到一个队列中,并使用多线程的方法来实现并发推送。
对于使用 SSE 实现多线程并发推送的 Node.js 代码,我们需要关心的是代码的性能,因为 Node.js 采用了单线程的架构,而服务器端需要承载着多个 SSE 客户端的请求。为了保证 SSE 推送的性能,应该充分考虑 Node.js 并发性的问题,采用合理的算法来解决。
总结
本文介绍了 Server-sent Events 的原理和实现方法,以及如何使用 Node.js 实现 SSE 的多线程并发推送。当然,我们可以根据业务需求进行变化探索,来实现更加优秀的 SSE 推送方式。同时,我们也应该充分考虑代码的性能,来确保 SSE 推送在各种网络条件下的表现。
来源:JavaScript中文网 ,转载请注明来源 本文地址:https://www.javascriptcn.com/post/65923916eb4cecbf2d71856f