告别轮询!用Node.js原生HTTP模块5分钟搞定SSE服务器推送(含完整代码)
5分钟用Node.js原生模块构建SSE实时推送系统含实战代码当我们需要在网页上展示实时更新的数据时——无论是文件上传进度、股票价格波动还是在线聊天消息——传统轮询方案就像不断刷新邮箱查看新邮件既低效又浪费资源。而WebSocket虽然强大但对于只需服务器单向推送的场景如同用火箭筒打蚊子。今天我要分享的SSEServer-Sent Events技术正是解决这类需求的优雅方案。1. 为什么选择SSE而非轮询或WebSocket在实时通信领域开发者常面临三种选择技术方案协议基础通信方向复杂度适用场景HTTP轮询HTTP双向低兼容性要求高的简单场景WebSocket独立协议双向高实时交互复杂应用SSEHTTP单向中服务器主动推送SSE的独特优势在于原生支持现代浏览器内置EventSourceAPI自动重连连接中断时会自动尝试恢复轻量协议基于标准HTTP无需额外端口或协议简单实现服务端只需设置正确的MIME类型实际项目中当只需要服务器向客户端推送数据时SSE的实现成本比WebSocket低50%以上2. 三行代码启动SSE服务器让我们用Node.js原生http模块创建一个最小化的SSE服务const http require(http); http.createServer((req, res) { if (req.url /stream) { res.writeHead(200, { Content-Type: text/event-stream, Cache-Control: no-cache, Connection: keep-alive }); setInterval(() res.write(data: ${new Date().toISOString()}\n\n), 1000); } }).listen(3000);关键点解析text/event-streamMIME类型是SSE的核心标识Cache-Control: no-cache确保代理服务器不会缓存事件每条消息以data:开头以两个\n结束3. 完整实现带重连机制的实战代码下面是一个包含错误处理和自定义事件的增强版实现const http require(http); let clientId 0; http.createServer((req, res) { if (req.url /sse) { const currentId clientId; console.log(客户端 ${currentId} 已连接); res.writeHead(200, { Content-Type: text/event-stream, Cache-Control: no-cache, Connection: keep-alive }); // 发送初始事件 res.write(event: handshake\n); res.write(data: ${JSON.stringify({ id: currentId })}\n\n); // 定时发送消息 const timer setInterval(() { res.write(data: ${new Date().toISOString()}\n\n); }, 3000); // 连接终止处理 req.on(close, () { clearInterval(timer); console.log(客户端 ${currentId} 已断开); }); } else { res.writeHead(404); res.end(); } }).listen(3000, () console.log(SSE服务运行在 http://localhost:3000));客户端对应实现script const es new EventSource(/sse); es.addEventListener(handshake, (e) { console.log(握手成功, JSON.parse(e.data)); }); es.onmessage (e) { document.body.innerHTML p${e.data}/p; }; es.onerror () { console.log(连接异常3秒后重试...); }; /script4. 生产环境最佳实践在实际部署时需要考虑以下关键因素性能优化使用res.flush()强制立即发送数据合理设置retry字段控制重试间隔单位毫秒res.write(retry: 5000\n\n);安全防护验证Origin头防止CSRF攻击为敏感数据添加lastEventId追踪if (req.headers[last-event-id]) { // 处理断线重连逻辑 }连接管理记录活跃连接以便广播消息const clients new Set(); clients.add(res); req.on(close, () clients.delete(res));消息格式进阶// 多行数据 res.write( event: statusUpdate id: 12345 data: {progress: 75} data: {stage: processing} );5. 常见问题解决方案Q1: 为什么我的连接几秒后就断开A: 可能是代理服务器超时设置导致解决方案定期发送注释保持连接活跃setInterval(() res.write(:keepalive\n\n), 15000);配置代理服务器允许长连接Q2: 如何向特定客户端发送消息A: 维护一个连接映射表const clientRooms {}; // 加入房间 clientRooms[userId] res; // 向特定用户发送 clientRooms[targetId]?.write(data: Private message\n\n);Q3: 如何传输二进制数据A: SSE本身只支持文本但可以通过Base64编码res.write(data: ${Buffer.from(binaryData).toString(base64)}\n\n);在最近的一个电商项目中我们用SSE实现了订单状态实时推送系统相比之前的轮询方案服务器负载降低了60%而代码量只有WebSocket实现的三分之一。特别是在移动网络不稳定的情况下SSE的自动重连机制显著提升了用户体验。