在Node.js容器中接收到的MQTT消息没有通过SSE推送到浏览器(Docker在 Raspberry Pi 5上运行)
问题 MQTT消息已成功发布到代理并被我的Node.js容器接收(日志中已确认),但它们并未通过服务器发送事件(SSE)传送到浏览器。浏览器的EventSource仍保持连接,但没有接收到任何事件,界面也从未更新。
预期行为 当我运行mosquitto_pub -t "openclaw/alert" -m "test message" 时,消息应当立即出现在浏览器界面上。
实际行为 消息到达Node.js容器(日志出现),但没有向浏览器推送任何内容。旧数据会一直显示在界面上。
环境
- 树莓派5
- Docker Compose
- Web应用:node:20-slim
- MQTT代理:eclipse-mosquitto:latest
服务器端代码(web-dashboard.js)
JavaScript
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");
});
客户端代码(浏览器)
JavaScript
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 logs 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
- 清除内存中的缓存
- 发送保活心跳包
- 检查Docker网络和端口绑定
有什么想法为什么SSE客户端在服务器处理了MQTT消息的情况下仍然接收不到事件吗?
解决方案
你的问题可能是 TCP套接字缓冲。Node.js以及底层的TCP层默认会对小数据包进行缓冲,这会破坏SSE的实时传输。你的服务器处理MQTT消息并写入响应流,但Nagle算法会延迟发送数据,直到积累足够字节或发生超时。
根据你对日志的描述,似乎 client.write() 的调用在服务端成功执行,但数据包在TCP层被缓冲,而不是立即发送。
尝试在两个层面禁用缓冲:
app.get('/events', (req, res) => {
res.writeHead(200, {
'Content-Type': 'text/event-stream',
'Cache-Control': 'no-cache',
'Connection': 'keep-alive'
});
res.flushHeaders(); // This forces buffer flush
req.socket.setNoDelay(true); // This disables Nagle's algorithm
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);
});
});
简而言之,flushHeaders() 能确保Node.js立即发送缓冲的数据,而 setNoDelay(true) 则禁用TCP的 Nagle算法,防止操作系统对小型SSE负载进行聚合。二者结合应能确保你的MQTT消息实时到达浏览器。
站内所有文章版权归属LeftHeroAI导航站,无授权禁止任何主体转载、抄袭、复制内容,亦不得私自架设镜像站点。一经侵权,本站将通过法律途径追责。