树莓派5 Docker环境下MQTT消息无法通过SSE推送到浏览器
问题
问题描述
MQTT消息已成功发布至代理并被Node.js容器接收(日志已确认),但始终无法通过Server-Sent Events(SSE)推送到浏览器。浏览器的EventSource保持连接,但无事件到达,UI始终不更新。
预期行为
执行mosquitto_pub -t "openclaw/alert" -m "test message"时,消息应立即显示在浏览器UI中。
实际行为
消息已到达Node.js容器(日志已输出),但未推送到浏览器,旧数据始终显示。
环境
- Raspberry Pi 5
- Docker Compose
- Web应用:node:20-slim
- MQTT代理:eclipse-mosquitto:latest
服务端代码(web-dashboard.js)
const express = require('express'); const mqttHandler = require('./mqtt-handler.js'); const app = express(); let sseClients = []; // SSE endpoint app.get('/events', (req, res) => { res.writeHead(200, { 'Content-Type': 'text/event-stream', 'Cache-Control': 'no-cache', 'Connection': 'keep-alive' }); res.write('data: {"type":"connected"}\n\n'); sseClients.push(res); const keepAlive = setInterval(() => { res.write(': keep-alive\n\n'); }, 15000); req.on('close', () => { clearInterval(keepAlive); sseClients = sseClients.filter(c => c !== res); }); }); // MQTT → SSE bridge mqttHandler.client.on('message', (topic, message) => { console.log(`📨 MQTT received: ${topic} → ${message.toString()}`); if (topic === 'openclaw/alert') { const payload = JSON.stringify({ type: 'alert', message: message.toString() }); sseClients.forEach(client => { client.write(`data: ${payload}\n\n`); }); } }); app.listen(3000, '0.0.0.0', () => { console.log("🌐 Dashboard connected to MQTT bus with real-time SSE"); });
客户端代码(浏览器)
const evtSource = new EventSource('/events'); evtSource.onmessage = function(event) { const data = JSON.parse(event.data); if (data.type === 'alert') { const el = document.getElementById('liveEvents'); el.innerHTML += '<br>' + data.message; el.scrollTop = el.scrollHeight; } };
Docker日志(openclaw-web-dashboard)
🌐 Dashboard connected to MQTT bus with real-time SSE 📨 MQTT received: openclaw/alert → 🧪 TEST ALERT - Broker is now running in Docker
每次发布消息都会生成上述日志,但浏览器从未接收。
已尝试方案
docker compose build --no-cache+--force-recreate- 清除内存缓存
- 添加保活ping
- 检查Docker网络与端口绑定
解决方案
1. 强制刷新SSE响应缓冲区
Node.js的res.write()在容器或代理环境下可能会缓冲输出,需手动调用flush()确保数据实时发送:
sseClients.forEach(client => { client.write(`data: ${payload}\n\n`); client.flush(); // 添加此行强制刷新 });
若使用Express 4.x+,可安装express-flush中间件增强兼容性。
2. 验证SSE初始连接与响应
打开浏览器开发者工具网络面板,查看/events请求的响应内容:
- 若未收到初始
connected消息,检查Docker端口映射是否正确(宿主机端口需绑定容器3000端口); - 若收到初始消息,确认后续推送的缓冲问题。
3. 排查客户端解析与错误
在客户端添加错误监听,排查JSON解析或连接异常:
evtSource.onerror = function(error) { console.error('SSE错误:', error); };
同时在服务端打印推送的payload,确认JSON格式无语法错误:
console.log('推送内容:', payload);
4. 配置反向代理支持长连接
若使用Nginx等反向代理,需禁用缓冲与缓存以适配SSE:
location /events { proxy_pass http://web-dashboard:3000; proxy_set_header Connection ''; proxy_http_version 1.1; chunked_transfer_encoding off; proxy_buffering off; proxy_cache off; }
5. 确认客户端连接稳定性
在服务端close事件中添加日志,检查客户端是否频繁断开:
req.on('close', () => { console.log('SSE客户端已断开'); clearInterval(keepAlive); sseClients = sseClients.filter(c => c !== res); });
若频繁断开,可将保活间隔缩短至5秒左右。
内容的提问来源于stack exchange,提问作者Steven Jafarian
相关产品推荐
相关产品推荐

