MQTT 客户端驱动
PlantPulse Edge 的 MQTT 客户端驱动是对 Eclipse Paho mqttv3 1.2.5 库进行封装的 Java 实现。 它订阅外部 IIoT broker(如 HiveMQ / Mosquitto / EMQX / AWS IoT Core / Azure IoT Hub 等)的 topic 并将消息保存在内存缓存中, 按照标签的轮询周期返回缓存值。写操作则立即发布。
源代码:plantpulse.driver.protocol.mqtt_client.*
| 类 | 职责 |
|---|---|
MQTTClientDriver | ProtocolDriver 实现 — connect/read/write/close、topic 缓存、惰性订阅 |
库依赖:lib/org.eclipse.paho.client.mqttv3-1.2.5.jar
与 Sparkplug B 的区别
PlantPulse 提供两种基于 MQTT 的驱动。
| 驱动 | 负载 | 使用场景 |
|---|---|---|
MQTT_CLIENT(本页) | 任意 String/JSON/binary | 通用 IIoT broker、自定义 topic、ASCII / JSON 负载 |
SPARKPLUG_B | Sparkplug B 负载(Protobuf) | Cirrus Link / Tahu / Ignition 标准,NBIRTH/DBIRTH 别名 |
Sparkplug B 自带线缆编解码器和别名学习逻辑,因此仅需通用 MQTT 的环境下 此驱动更轻量,仅使用标准 Paho API 即可满足需求。
操作流程
broker ──PUBLISH──> Paho MqttClient ──MqttCallback.messageArrived(topic, payload)──> Driver
│
└─ lastValueByTopic.put(topic, new String(payload))
PlantPulse 폴러 ──read(addr)──> lastValueByTopic[addr] (in-memory hit)
write(addr, value) ──Paho.publish(topic, value, qos, retain=false)──> broker
首次调用 read 时若缓存未命中,则进行惰性订阅(使用 QoS 选项值)并返回空字符串 — 从下一周期起
实际值才会到达。tag_map 中注册的 topic 将在连接后立即通过 preSubscribeFromTagMap()
批量订阅,因此第一个周期即可收到值(取决于消息发布频率)。
选项
| 键 | 默认值 | 说明 |
|---|---|---|
username | (无) | broker 认证用户名 |
password | (无) | broker 认证密码 |
tls | false | true → ssl://(默认 8883),false → tcp://(默认 1883) |
qos | 0 | 订阅 / 发布 QoS(0 / 1 / 2)。超出范围的值自动限制。 |
keep-alive | 60 | 保活周期(秒)— MqttConnectOptions.setKeepAliveInterval |
clean-session | true | MqttConnectOptions.setCleanSession |
client-id | (自动) | 显式客户端 ID。未指定时自动生成 PP-<opc_id>-<random6>(≤23 字符) |
MqttConnectOptions.setAutomaticReconnect(true) 始终启用,因此暂时性 broker 中断后会自动重连
(应用 Paho 默认退避策略)。
Address 映射
| address | 操作 |
|---|---|
factory/line1/temp | 单个 topic — 此 topic 的最后一条消息 |
device/+/status | 通配符(单层)— 匹配 topic 的最后一条消息(最近到达) |
factory/# | 通配符(多层)— 最后一段,所有子 topic |
"" / null | 返回空 |
通配符注意:Paho 的回调不会按实际到达的 topic 分别缓存条目,
而是以到达的 topic 本身作为键。即使用 device/+/status 订阅,
lastValueByTopic 中会分别存储 device/sensor01/status、device/sensor02/status,
而用户以 通配符 topic 本身读取时,缓存中无条目(通配符本身不是
发布的目标)。注册通配符模式时,应按 device/+/status 方式注册,然后
通过 read 的结果单独追踪最后到达 topic 的值 — 常规使用中建议按 topic 逐一
显式注册。
写操作(发布)
client.publish(topic, value.getBytes(), qos, /*retain=*/ false);
value的字节将原样作为负载发送(默认字符集为 UTF-8)。- 固定为
retain=false,无法更改 — 由于 broker 不保留最后一条消息, 后续才开始订阅的客户端在下次写入发生前无法收到值。 对于需要保留消息的消费者,请在 broker 侧配置桥接或单独发布器。 - QoS 按选项值原样使用。若为 QoS 1/2,Paho 会在内部重传直至确认。
连接生命周期
| 方法 | 操作 |
|---|---|
connect() | 应用选项 → 组装 broker URL → MqttClient.connect() → tag_map 批量订阅 |
read() | 缓存未命中时惰性订阅 → 缓存命中则返回值 |
write() | 发布(retain=false) |
close() | disconnect() + close()(尽力而为,忽略异常)→ 清空缓存 |
connectionLost(cause) | 回调中触发 setConnected(false) — Paho 自动尝试重连 |
限制及后续工作
- mTLS / 客户端证书 — 目前仅支持用户名/密码。对于 AWS IoT 等 X.509 mTLS 环境,需要单独
配置 truststore / keystore(Paho
MqttConnectOptions.setSocketFactory尚未暴露)。 - retained / will 消息 — 当前固定为
retain=false,未设置 LWT。 - 通配符缓存分离 — 以通配符模式本身读取时结果为空(按到达 topic 分别存条目)。
- JSON path 提取 — 即使负载为 JSON,驱动也仅返回原始字符串。JSONPath 提取需在
format/ 计算阶段处理(或使用单独解析节点)。 - 负载编码 — 当前使用平台默认字符集。二进制负载可能损坏 UTF-8 →
未来考虑暴露
payload-encoding选项(utf8/iso-8859-1/base64)。
测试位置
test/java/plantpulse/driver/protocol/mqtt_client/MQTTClientDriverTest.java — 31 个测试。
- 初始状态 / 元信息(
isConnected/isWriteSupported/isExternal/getDriverSource) - 未连接时 read/write/null/empty 安全性
getDebugBaseUrl— tcp/ssl、显式端口、默认端口(1883/8883)- 选项解析 — username/password/tls/keep-alive/clean-session/qos/client-id、无效值 → 默认值
- 静态辅助函数 —
clampQos、defaultClientId(长度/随机/null)、nullIfEmpty、parseInt、parseBool - 常量校验(
DEFAULT_PORT、DEFAULT_PORT_TLS、DEFAULT_KEEP_ALIVE_SEC、DEFAULT_QOS)
依赖真实 broker 的集成测试单独进行 — 本单元测试无需外部 broker 即可运行。