Mosquitto Dashboard 本地开发与运行指南:基于 HTTP API 的 Web 监控界面实战
【免费下载链接】mosquittoEclipse Mosquitto - An open source MQTT broker项目地址: https://gitcode.com/gh_mirrors/mos/mosquitto
Mosquitto Dashboard 是 Eclipse Mosquitto 仓库中附带的一套纯前端 Web 图形界面,它不直接订阅 MQTT 主题,而是通过调用 broker 的 HTTP API(/api/v1/systree与/api/v1/listeners)来渲染实时监控面板,包括消息收发速率、客户端连接数、监听器状态等。本文基于 dashboard/README.md 的完整说明,结合仓库内前端源码与 src/http_api.c 的 broker 端实现,逐步骤讲解如何在本地把该控制台跑起来、如何接入真实 broker,并深入剖析其数据采集、图表渲染与页面交互的底层原理。
一、Dashboard 是什么
dashboard/目录下的代码是一套「简单且基于 Web 的 Mosquitto 图形用户界面」(README 原文如此描述),它的目标不是替代mosquitto_sub/mosquitto_pub这类命令行工具,而是提供一块可视化的监控看板,用于观察 broker 的运行状态。
与常见的"通过 WebSocket 桥接 MQTT"方案不同,本 Dashboard 的数据来源是Mosquitto 内置的 HTTP API。从 dashboard/src/app/consts.js 可以看到两个核心端点定义:
const SYSTOPIC_ENDPOINT = "/api/v1/systree"; const LISTENERS_ENDPOINT = "/api/v1/listeners";而这两个端点在 broker 端的实现位于 src/http_api.c 的http_api__process_api()中:/api/v1/systree返回全部$SYS系统主题的当前值,/api/v1/listeners返回各监听器配置。因此,运行 Dashboard 的前提是 broker 启动了protocol http_api类型的监听器。
二、本地开发环境准备
README 给出的依赖非常简单——Dashboard 是纯静态前端,不依赖 Node.js 构建链,仅需要 Tailwind CSS 与一个静态文件服务器。具体依赖如下(存放于dashboard/src/lib目录,仓库已随附):
| 依赖 | 版本 | 用途 |
|---|---|---|
| chartjs | 4.3.0 | 折线图/面积图渲染 |
| chartjs-plugin-zoom | 2.2.0 | 图表的缩放(滚轮/双指)与平移 |
| hammer.js | 2.0.8 | 触屏手势支持(为 zoom 插件提供 pinch 手势) |
对应文件为 dashboard/src/lib/chart.umd.js、dashboard/src/lib/chartjs-plugin-zoom.min.js 与 dashboard/src/lib/hammer.min.js,无需自行下载。
另外还需要全局安装 Tailwind CSS 3(用于把index.html中的 Tailwind 类编译为 CSS):
npm -g install tailwindcss@3三、五步跑通本地开发环境
README 给出了完整的本地开发步骤,下面按原文档顺序展开,并补充每一步的细节与原理。
第 1 步:生成 Tailwind CSS
进入dashboard/src目录,执行:
cd dashboard/src tailwindcss -i ./css/styles.css -o ./tailwind/styles.css该命令以 dashboard/src/css/styles.css 为输入(Tailwind 指令入口),扫描 dashboard/src/tailwind.config.js 中content声明的./*.html与./*.js,把实际用到的工具类生成到 dashboard/src/tailwind/styles.css。也就是说,每次修改了 HTML/JS 中的 Tailwind 类名后,都需要重新执行这条命令,否则新类不会出现在最终样式文件中。如果后续新增了 Tailwind 工具类而页面样式未生效,先检查这一步是否已重新执行。
第 2 步:准备 HTTP API 数据源
README 第 3 步是「运行 mosquitto http api mock」——即先不连真实 broker,用 mock 数据验证前端。这一步有两种做法:
- 方式 A(推荐,最贴近真实):直接启动一个开启了 HTTP API 的 Mosquitto broker,见下文「接入真实 broker」一节,
mosquitto自身就是最可信的 API 服务; - 方式 B(仅前端调试):自行实现一个简单的 mock 服务,返回
systree与listeners两个 JSON 接口。
第 3 步:修改 API 端点地址
README 第 4 步要求修改 dashboard/src/app/consts.js 中的 Mosquitto API 端点。默认值如下:
const SYSTOPIC_ENDPOINT = "/api/v1/systree"; const LISTENERS_ENDPOINT = "/api/v1/listeners";由于consts.js中的端点默认是相对路径,当前端由python3 -m http.server在3000端口提供服务、而 broker API 在另一个端口(如8080)时,需要把这里改为绝对地址,例如:
const SYSTOPIC_ENDPOINT = "http://localhost:8080/api/v1/systree"; const LISTENERS_ENDPOINT = "http://localhost:8080/api/v1/listeners";注意:跨端口访问会触发浏览器同源策略(CORS)。从 src/http_api.c 的实现看,HTTP API 端点默认不携带 CORS 响应头,因此最稳妥的做法是让静态页面与 API 同源(见第 5 步的替代方案),或者由 mock 服务自行附带Access-Control-Allow-Origin头。
第 4 步:启动静态文件服务器
进入dashboard/src,运行任意静态服务器,README 给出的示例是 Python:
cd dashboard/src python3 -m http.server 3000随后浏览器访问http://localhost:3000即可看到index.html(总览看板),访问http://localhost:3000/listeners.html查看监听器页。页面初始化逻辑集中在 dashboard/src/app/index.js:
document.addEventListener("DOMContentLoaded", () => { new Sidebar(); new MosquittoDashboard(); });第 5 步(替代方案):让 Mosquitto 直接托管前端
其实 Dashboard 与 broker 同源有更优雅的路径:Mosquitto 的http_api监听器支持http_dir配置项,可以直接把静态文件目录交给 broker 托管。此时访问 broker 的 HTTP 端口就能同时拿到页面与/api/v1/*数据,彻底规避 CORS 与手动改consts.js的问题,部署形态见下文第五节。
四、前端源码结构导读
整个dashboard/src的目录组织如下(与 README 描述的src/lib依赖目录对应):
| 路径 | 职责 |
|---|---|
| dashboard/src/index.html | 总览看板页面(图表、指标卡、banner) |
| dashboard/src/listeners.html | 监听器列表页面 |
| dashboard/src/app/index.js | 页面入口:初始化 Sidebar 与 Dashboard、网格/单列布局切换、banner 探测 |
| dashboard/src/app/dashboard.js | 核心:数据拉取、$SYS主题→DOM/图表的映射、Chart.js 图表管理 |
| dashboard/src/app/listeners.js | 监听器信息展示与连接命令生成 |
| dashboard/src/app/consts.js | 常量:API 端点、轮询/保留窗口等时间参数 |
| dashboard/src/app/sidebar.js | 侧边导航 |
| dashboard/src/utils/utils.js | fetchData、时间格式化、数字美化等工具函数 |
| dashboard/src/utils/queue.js | 串行任务队列,防止图表更新互相竞争 |
| dashboard/src/utils/assert.js | 断言工具 |
| dashboard/src/css/styles.css | Tailwind 输入样式 |
| dashboard/src/tailwind/styles.css | Tailwind 编译产物 |
dashboard/src/lib/*.js | 第三方库(chartjs / zoom / hammer) |
五、接入真实 broker:配置 HTTP API 监听器
要让 Dashboard 显示真实数据,需要启动一个启用 HTTP API 的 Mosquitto。相关配置项在仓库根目录的 mosquitto.conf 中有完整注释,核心为listener与protocol http_api:
# listener 端口 可选: ip 地址/主机名/unix socket 路径 listener 8080 127.0.0.1 # 该监听器协议: mqtt / websockets / http_api protocol http_api # 指定由该监听器托管的静态文件目录(可选) http_dir /path/to/dashboard/src关于protocol http_api,mosquitto.conf 的注释明确指出:它将监听器启动为一个非常简单的 Web 服务器,可处理部分 HTTP API 请求;该监听器支持 TLS,但当前无法进行认证,因此强烈建议只绑定到 loopback 地址(如127.0.0.1)。
完成配置后启动:
mosquitto -c /path/to/mosquitto.conf验证 API 是否就绪:
curl http://127.0.0.1:8080/api/v1/systree curl http://127.0.0.1:8080/api/v1/listeners curl http://127.0.0.1:8080/api/v1/version若配置了http_dir指向dashboard/src,则直接访问http://127.0.0.1:8080/与http://127.0.0.1:8080/listeners.html即可看到完整界面,且无需修改consts.js。从 src/http_api.c 的实现可见,若http_dir未显式配置,代码还会尝试回退到编译期宏HTTP_API_DIR指定的目录。
broker 端 API 的响应内容
/api/v1/systree的实现(src/http_api.c)会遍历内部指标表metrics[],把每个$SYS主题的当前值以{"$SYS/broker/clients/total": 5, ...}的形式序列化为 JSON,并额外附上$SYS/broker/uptime。注意该端点依赖编译开关WITH_SYS_TREE,未开启时会返回 404。
/api/v1/listeners的实现(src/http_api.c 附近)会遍历db.config->listeners,为每个监听器输出端口(或 Unix socket 路径)、协议、TLS、mTLS、allow_anonymous等字段,protocol为httpapi的监听器也会被标记出来。
六、看板的数据流与刷新机制
理解了 API 端点后,再看前端如何消费这些数据。dashboard/src/app/dashboard.js 中的checkForDataUpdates()是核心轮询函数:
- 调用
fetchData(SYSTOPIC_ENDPOINT, { cache: "no-store" })拉取最新$SYS快照(dashboard/src/utils/utils.js 中的fetchData会强制附带Accept: application/json头); - 从中提取
$SYS/broker/version更新 broker 版本号,并根据请求成败把「broker-status」置为 Online/Offline; - 调用
getElementsToUpdate(sysTopics),只更新发生变化的指标与图表——该方法内部维护了一份lastSysTopics快照,逐个$SYS主题比对前后值,有变化才写入待更新集合; - 调用
updateCharts(...)推进所有图表的 data/labels,并将最新数据写入sessionStorage。
轮询节奏由 dashboard/src/app/consts.js 控制:
const INTERVAL_5SECS_IN_MILLISECONDS = 1000 * 5; // 每 5 秒拉取一次 const KEEP_DATAPOINTS_FOR_INTERVAL = 1000 * 60 * 60 * 2; // 原始数据最多保留 2 小时 const MAX_POINTS_IN_CHART = 5_000; // 单图表最多 5000 个点 const CHART_UPDATE_INTERVAL_IN_MILLISECONDS = 1000 * 60; // 强制刷新周期 1 分钟startDataUpdates()(dashboard/src/app/dashboard.js)的实现细节值得注意:首次拉取后,它会计算「下一个能被 5 秒整除的时间点」并据此安排setTimeout,从而让各浏览器标签页的数据点尽量对齐;每次拉取完成后,会用「5 秒减去本次请求耗时」作为下次超时时间,再叠加 Chart.js 动画时长(CHARTJS_ANIMATION_DURATION_MS = 400)的缓冲,并通过 dashboard/src/utils/queue.js 的串行队列执行,避免图表更新与动画进入竞争状态。
$SYS主题与界面元素的映射
getElementsToUpdate()中维护了一张详尽的「主题 → 界面」映射表,例如:
$SYS主题 | 对应界面元素 |
|---|---|
$SYS/broker/uptime | broker-uptime(格式化为「X days X hours …」) |
$SYS/broker/clients/connected | clients-connected +chart-clients-connected |
$SYS/broker/clients/disconnected | clients-disconnected +chart-clients-disconnected |
$SYS/broker/messages/sent/received | 独立图 +chart-message-overview双线对比图 |
$SYS/broker/load/messages/sent/1min/received/1min | 速率图 +chart-message-rate-overview对比图 |
$SYS/broker/publish/messages/dropped | chart-messages-dropped |
$SYS/broker/heap/current/maximum | 堆内存展示 |
$SYS/broker/subscriptions/count | 订阅总数 |
其中,发送/接收速率图使用$SYS/broker/load/messages/{sent,received}/1min这类「每分钟消息数」指标,映射关系定义在updateMatchingChart()中(dashboard/src/app/dashboard.js),它还会把「发送」与「接收」两幅图配对联动更新,保证对比图两条曲线时间轴一致。
原始数据与平滑数据
看板右上角提供了「Show Raw Data / Show Smoothed Data」切换(addToggle())。每个图表同时维护两套数据:
- raw(原始):每次拉取到的真实数值,直接追加;
- smoothed(平滑):只有满足一定条件才插入新点——当值的变化幅度超过 20%(
datapointsAreSufficientlyDifferent),或与前一个点的时间间隔超过 5 分钟(SMOOTHED_CHART_UPDATE_INTERVAL_IN_MILLISECONDS)时。
这种「事件驱动 + 定时兜底」的策略(见addSmoothedDataPoint())让平滑图既能过滤抖动、又不会长时间静止显示陈旧数据。图表本身基于 Chart.js 折线图,启用了chartjs-plugin-zoom的滚轮/双指缩放与平移,且每个图表都有独立的缩放/平移/重置按钮(handleChartAction()中的chart.zoom(1.2)、chart.pan({x:100})、chart.resetZoom()等)。
会话级持久化
Dashboard 把图表数据、最新$SYS快照、布局偏好(网格/单列)与「原始/平滑」选项全部写入sessionStorage(updateStore(),dashboard/src/app/dashboard.js)。刷新页面时composeDashboardObject()会优先从sessionStorage恢复历史曲线,而不是从空白开始——因此浏览器标签页关闭前,图表数据不会丢失;但关闭标签页后sessionStorage即被清空,重新打开将重新开始累积曲线。此外,index.js会把「单列视图」偏好存入sessionStorage,下次访问时自动恢复。
七、监听器页面(listeners.html)功能解读
listeners.html由 dashboard/src/app/listeners.js 驱动:页面加载时请求LISTENERS_ENDPOINT,为每个监听器生成一张卡片,展示端口(或 Unix socket 路径)、协议、是否启用 TLS/mTLS、是否允许匿名连接等字段。
两个值得注意的能力:
- 匿名监听器告警:
displayListeners()会统计allow_anonymous为真的监听器数量,若大于 0,则会在顶部显示带「UNSAFE」红色徽标的计数,提示当前存在未认证开放的端口; - 一键复制连接命令:
generateConnectionCommand(listener, "mosquitto_pub")会根据监听器的协议(MQTT / WebSockets / Unix socket)、TLS/mTLS、匿名与否,动态拼出可直接使用的mosquitto_pub命令模板,例如:
# TLS + 需认证的 WebSocket 监听器会生成类似: mosquitto_pub -h <host> -p 9001 --ws --cafile <ca-crt.pem> \ --cert <client-crt.pem> --key <client-key.pem> \ -u <username> -P <password> -t <topic> -m <message>卡片上的复制按钮把命令写入剪贴板(copyToClipboard()优先使用navigator.clipboard,并降级到document.execCommand("copy")),方便直接粘贴到终端测试连通性。若监听器协议为httpapi,则不生成 pub 命令,而是提示「使用 REST 调用代替 mosquitto_pub/sub」。
八、常见问题与注意事项
结合 README 步骤与源码实现,本地开发时容易踩的坑集中在以下几点:
- Tailwind 类不生效:修改了 HTML/JS 后未重新执行
tailwindcss -i ./css/styles.css -o ./tailwind/styles.css; - 数据不显示/接口 404:确认 broker 编译时启用了
WITH_SYS_TREE(/api/v1/systree依赖它),且监听器配置了protocol http_api; - 跨域被拦:前端由独立端口服务时访问
http://localhost:8080/api/...会触发 CORS;最省事的解法是用http_dir让 broker 同时托管页面与 API,实现同源访问; - 刷新后曲线消失:这是
sessionStorage的预期行为——数据只在当前标签页会话内保留(默认保留最近 2 小时、最多 5000 个点); - Firefox 下快速切换页面可能短暂弹错:
registerAbortController()(dashboard/src/utils/utils.js)的源码注释明确提到,Firefox 在导航前不会可靠执行 abort 回调,可能导致请求已取消但仍弹出一次错误提示,属已知浏览器行为,不影响功能。
九、小结
本文完整复现并扩展了 dashboard/README.md 的本地开发流程:从安装 Tailwind、生成 CSS、修改 dashboard/src/app/consts.js 中的 API 端点,到启动静态服务器或直接由protocol http_api监听器配合http_dir托管前端。在此基础上,结合 dashboard/src/app/dashboard.js、dashboard/src/app/listeners.js 与 src/http_api.c 的源码,梳理了$SYS指标到图表的映射、5 秒轮询与平滑采样策略、sessionStorage持久化,以及监听器页面的命令生成与匿名告警机制。按照上述步骤,你可以在几分钟内把 Mosquitto 的实时运行状态变成一组可视化图表,并将其作为本地监控或演示环境的一部分。
【免费下载链接】mosquittoEclipse Mosquitto - An open source MQTT broker项目地址: https://gitcode.com/gh_mirrors/mos/mosquitto
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考