接入参数
设备生产配置应使用下列公网端点。
8883 是原生 MQTTS,不是 HTTPS 或 WSS。Web 门户使用 HTTPS 443;明文 MQTT 1883 仅限服务器本机,禁止对公网开放。
| 项目 | 当前值 | 说明 |
|---|---|---|
| 公网并发连接 | 不设软件上限 | 实际容量由服务器资源、消息频率和负载测试结果决定 |
| 单条消息 | 262144 字节 | 约 256 KiB,不用于图片或固件传输 |
| 离线队列 | 1000 条 / 1 MiB | 超出后不能依赖 Broker 完成全部补传 |
| 持久会话 | 7 天 | 超过期限后自动过期 |
接入准备
管理员应为每台设备单独提供接入材料。
- 1创建真实设备
管理员在“设备管理”填写唯一
deviceId、名称和位置。 - 2保存一次性凭据
创建成功后立即保存 Client ID、用户名、密码及 Topic;关闭窗口后不会再次显示原密码。
- 3安装公共根证书
从凭据窗口下载
ca.crt,将其配置为 Broker 信任锚。 - 4校准设备时间
TLS 证书校验依赖正确时间,设备启动后应先完成可靠时间同步。
ca.crt 是可以分发的公共证书。设备不需要 server.crt、server.key 或 CA 私钥,任何私钥都不能复制到设备。
97:E3:76:FA:1B:14:F6:73:15:A9:FA:6C:47:9E:2B:45:37:38:DD:0B:10:1B:50:C7:BA:7A:DB:EA:2A:61:F7:C5首次下发 CA 时请通过另一个可信渠道核对指纹;证书轮换后本文档也需要同步更新。
Topic 规范
每台设备只能访问自己的 Topic 前缀。
devices/{deviceId}/#| Topic | 设备方向 | QoS | Retain | 用途 |
|---|---|---|---|---|
devices/{deviceId}/telemetry | 发布 | 0 / 1 | 否 | 周期遥测 |
devices/{deviceId}/status | 发布 | 1 | 是 | 在线、离线状态 |
devices/{deviceId}/event | 发布 | 1 | 否 | 告警和事件 |
devices/{deviceId}/command | 订阅 | 1 | 否 | 平台下发命令 |
devices/{deviceId}/command/response | 发布 | 1 | 否 | 命令执行结果 |
- Topic 区分大小写,
Device-001与device-001不相同。 - 生产 ACL 应按发布和订阅方向收紧,不允许设备互相访问。
- 命令 Topic 禁止 Retain,避免设备重连后执行旧命令。
- 当前 PoC 测试账号只允许访问自己的测试前缀;正式设备不得复用。
遥测数据格式
Broker 暂不校验结构,建议统一使用 UTF-8 JSON。
{
"ts": 1787649000000,
"seq": 1024,
"metrics": {
"temperature": 24.6,
"humidity": 61.2,
"pressure": 101.3
},
"status": "online"
}
ts设备采样时间,Unix 毫秒时间戳。
seq启动后的递增序号,用于发现丢包或乱序。
metrics点位名称到数值的映射,数值不要写成字符串。
status可选业务状态,不替代 MQTT 连接在线状态。
现场数据来自 Modbus 时,建议由边缘网关完成寄存器读取、基址处理、量纲换算和质量判断,再将标准 JSON 发布至 MQTTS。
连接与在线状态
适用于测试机和后续扩容服务器的连接建议。
一台设备一个持久连接,不要每次上报重新连接。
结合可靠的断线检测,避免过短心跳产生额外负载。
普通遥测推荐 QoS 1;可丢失的高频数据可选 QoS 0。
使用 1–60 秒指数退避与随机抖动,避免设备同时重连。
PoC 周期遥测建议不快于每 5 秒一次,告警可立即发送。
状态可保留;普通遥测、事件和命令默认不保留。
定期发布在线状态或遥测;连续 120 秒未收到有效消息时平台判定离线。
推荐遗嘱消息
- Topic
devices/{deviceId}/status- Payload
{"status":"offline"}- QoS
- 1
- Retain
- true
连接成功后主动发布带时间戳的 online 状态,并每 30–60 秒重发一次或在该时间内发布遥测;正常关机前主动发布带时间戳的 offline。异常掉线由 Broker 代发遗嘱,平台默认 120 秒无有效消息后判定离线。
客户端示例
所有示例都启用服务端证书校验,且不包含真实凭据。
MQTTX · 模拟设备在线
- 先断开连接,在连接编辑页展开 Will Message;Topic 填写
devices/aigelun-device-001/status。 - 遗嘱 Payload 填写
{"status":"offline"},QoS 选择1,开启 Retain,然后重新连接。 - 在发布区使用同一状态 Topic,QoS 选择
1,开启 Retain。 - 发布
{"status":"online","ts":1787737442000};实际调试时把ts换成当前 Unix 毫秒时间戳。 - 每 30–60 秒重发在线状态或遥测。正常断开前主动发布带时间戳的
offline;异常断网则由 Broker 发送遗嘱。
MQTTX 顶部显示 Connected 只代表 Broker 认证通过。至少向状态或遥测 Topic 成功发布一条消息后,门户才会标记设备在线;遥测 Topic 不要开启 Retain。
Mosquitto CLI · 先订阅
mosquitto_sub \
-h 47.94.172.189 \
-p 8883 \
-V mqttv311 \
--cafile ./ca.crt \
-u '<设备账号>' \
-P '<设备密码>' \
-i '<deviceId>-debug-sub' \
-t 'devices/<deviceId>/#' \
-q 1 -v
Mosquitto CLI · 再发布
mosquitto_pub \
-h 47.94.172.189 \
-p 8883 \
-V mqttv311 \
--cafile ./ca.crt \
-u '<设备账号>' \
-P '<设备密码>' \
-i '<deviceId>-debug-pub' \
-t 'devices/<deviceId>/telemetry' \
-m '{"ts":1787649000000,"metrics":{"temperature":24.6,"humidity":61.2}}' \
-q 1
共享计算机上,-P 参数可能被其他进程看到。生产设备应从安全存储或权限受限的配置读取密码,不要把密码固定写入脚本。
Python · paho-mqtt 2.x
import json
import os
import ssl
import time
import paho.mqtt.client as mqtt
device_id = os.environ["DEVICE_ID"]
status_topic = f"devices/{device_id}/status"
telemetry_topic = f"devices/{device_id}/telemetry"
client = mqtt.Client(
callback_api_version=mqtt.CallbackAPIVersion.VERSION2,
client_id=device_id,
protocol=mqtt.MQTTv311,
)
client.username_pw_set(
os.environ["MQTT_USERNAME"],
os.environ["MQTT_PASSWORD"],
)
client.tls_set(
ca_certs="ca.crt",
cert_reqs=ssl.CERT_REQUIRED,
tls_version=ssl.PROTOCOL_TLS_CLIENT,
)
client.will_set(
status_topic,
json.dumps({"status": "offline"}),
qos=1,
retain=True,
)
client.connect("47.94.172.189", 8883, keepalive=60)
client.loop_start()
client.publish(
status_topic,
json.dumps({"status": "online", "ts": int(time.time() * 1000)}),
qos=1,
retain=True,
).wait_for_publish()
client.publish(
telemetry_topic,
json.dumps({
"ts": int(time.time() * 1000),
"seq": 1,
"metrics": {"temperature": 24.6, "humidity": 61.2},
}),
qos=1,
).wait_for_publish()
Node.js · mqtt
const fs = require('node:fs');
const mqtt = require('mqtt');
const deviceId = process.env.DEVICE_ID;
const statusTopic = `devices/${deviceId}/status`;
const client = mqtt.connect('mqtts://47.94.172.189:8883', {
protocolVersion: 4,
clientId: deviceId,
username: process.env.MQTT_USERNAME,
password: process.env.MQTT_PASSWORD,
ca: fs.readFileSync('./ca.crt'),
rejectUnauthorized: true,
keepalive: 60,
reconnectPeriod: 3000,
will: {
topic: statusTopic,
payload: Buffer.from(JSON.stringify({ status: 'offline' })),
qos: 1,
retain: true
}
});
client.on('connect', () => {
client.publish(
`devices/${deviceId}/telemetry`,
JSON.stringify({
ts: Date.now(),
seq: 1,
metrics: { temperature: 24.6, humidity: 61.2 }
}),
{ qos: 1, retain: false }
);
});
Node.js 的 protocolVersion: 4 表示 MQTT 3.1.1;使用 MQTT 5.0 时改为 5。
故障排查
按网络、TLS、认证、ACL 的顺序定位问题。
| 现象 | 常见原因 | 处理方式 |
|---|---|---|
| 超时或拒绝 | 安全组、出口网络、服务状态或端口错误 | 确认连接 47.94.172.189:8883,不要使用 1883/443 |
| 证书校验失败 | 未加载正确 ca.crt | 配置私有 CA 信任链,禁止跳过校验 |
| IP/hostname mismatch | 地址不在证书 SAN 中 | 公网必须按 47.94.172.189 连接 |
| 证书未生效或过期 | 设备时间错误或证书到期 | 同步时间并检查证书有效期 |
| Bad username/password | 凭据错误、尾部换行或匿名连接 | 核对凭据,从文件读取密码时去除行尾换行 |
| Not authorized | Topic 超出账号 ACL | 使用分配的设备前缀并核对大小写 |
| 反复掉线 | 两个客户端复用同一 Client ID | 为每个同时在线连接分配唯一 Client ID |
| 浏览器无法连接 | 8883 是原生 MQTT TCP,不是 WSS | 浏览器后续通过平台 SSE/WSS 适配层接入 |
Windows 端口检查
Test-NetConnection 47.94.172.189 -Port 8883
该命令只能检查 TCP 端口,不能验证 MQTT 登录、Topic ACL 或证书。
安全与容量要求
正式接入 100 台设备前必须执行。
每台正式设备使用独立账号、密码、Client ID 和 ACL。
不在网页、前端代码、日志、Topic、URL、截图或 Git 中保存密码。
始终验证 Broker 证书,禁止 --insecure 或关闭校验。
只分发公开 ca.crt,绝不分发 CA 私钥或服务端私钥。
安全组不开放 1883;8883 最好按设备出口 IP 收窄。
泄露的凭据立即吊销和轮换,不以修改设备名称代替。
平台与 Broker 不设置设备在线数量的软件上限。当前测试机建议只承载约 10 台同时在线设备;迁移到更强服务器后,应按目标规模完成持续连接、集体重连、消息峰值、磁盘、离线补传和证书轮换测试。