You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

树莓派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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.01 16:34:53