在 EMQX 中使用规则引擎记录 MQTT 消息日志(正确处理 Retain 参数)

当你在 EMQX 中创建规则:

SELECT * FROM "#"

并配置动作将消息转发到“日记主题”(如 journal/log)时,原始订阅者仍然应该能正常收到消息
如果你发现“所有消息都被日记主题消费了”,很可能是对 Retain(保留消息) 参数的误解或配置不当所致。


一、核心原理澄清

✅ 规则引擎不会“消费”消息

  • EMQX 的规则引擎是旁路监听机制,不会拦截或阻止原始消息投递。
  • 原始订阅者(如订阅 sensor/+/temp 的客户端)仍会正常收到实时消息。
  • 如果订阅者收不到消息,请排查:

    • 客户端是否真正订阅了对应主题
    • 是否有通配符冲突
    • 是否误用了 do_not_publish = true(默认为 false)

二、Retain 参数详解

什么是 Retain?

  • MQTT 协议中的 保留消息(Retained Message) 机制。
  • 当发布消息时设置 Retain = true,Broker 会为该主题保存最新一条消息
  • 新订阅者首次订阅该主题时,会立即收到这条保留消息(即使没有新发布)。

Retain 的典型用途

  • 传递设备“当前状态”(如开关状态、传感器最新值)
  • 不适用于日志、事件流等“流水型”数据

三、为什么 Retain 可能导致“消息被消费”的错觉?

现象原因
客户端一订阅就收到一条旧消息republish 动作中设置了 Retain = true
日志消费者处理异常或忽略消息误将保留的旧日志当作实时消息
多个客户端订阅通配符主题(如 #一次性收到大量 retain 消息,造成混乱
以为原始消息“没了”实际是 retain 行为干扰了预期,原始投递仍在进行
🔍 关键点:Retain 不会阻止原始消息投递,它只影响新订阅者的初始行为

四、正确配置日志规则(推荐)

1. SQL 规则(可选增强)

SELECT
  clientid,
  username,
  topic,
  payload,
  qos,
  now as ts
FROM
  "#"
WHERE
  topic != 'sys/log/mqtt'  -- 避免日志消息被自己再次捕获(防循环)

2. 动作:消息重新发布(republish)

配置项推荐值说明
目标主题sys/log/mqtt使用独立命名空间,避免与业务主题冲突
QoS01日志通常用 QoS 0(高效),关键日志用 QoS 1
Retainfalse日志不是状态,不要保留!
Payload自定义 JSON(见下方)结构化便于后续处理

Payload 模板示例:

{
  "clientid": "${clientid}",
  "topic": "${topic}",
  "payload": ${payload},
  "qos": ${qos},
  "timestamp": "${ts}"
}

五、如何验证和调试

1. 测试原始消息是否正常投递

# 发布测试消息
mosquitto_pub -t "sensor/1/temp" -m '{"value": 25.5}'

# 订阅原始主题(新开终端)
mosquitto_sub -t "sensor/+/temp" -v
# ✅ 应能实时收到消息

2. 检查日志主题是否收到副本

mosquitto_sub -t "sys/log/mqtt" -v
# 应收到结构化日志消息

3. 清除 retain 消息(如有残留)

# 清除 journal/log 的 retain 消息
mosquitto_pub -t "journal/log" -n -r
# -n: 空 payload,-r: retain=true → 清除该主题的 retain

六、最佳实践总结

项目推荐配置理由
Retainfalse日志是流水,非状态,无需保留
QoS0(默认)高效;若需可靠日志,选 1
目标主题独立前缀(如 sys/log/...避免被 #+ 通配符误匹配
PayloadJSON 结构化包含元数据(topic、clientid、ts 等)
规则 WHERE过滤日志主题自身防止规则循环触发

七、常见误区排查清单

  • [ ] 是否在 republish 动作中误设 Retain = true
  • [ ] 日志消费者是否订阅了通配符(如 #),导致收到大量 retain 消息?
  • [ ] 原始订阅者是否真的在线并正确订阅了主题?
  • [ ] 是否有多个规则或客户端意外消费了消息?
  • [ ] 是否启用了共享订阅($share/...),导致消息被分摊?

八、结论

Retain 设置为 false 是日志场景的标准做法。
正确配置后,EMQX 会:

  • 正常投递原始消息给所有订阅者 ✅
  • 同时复制一份结构化日志到指定主题 ✅
  • 不会产生“消息被消费”的错觉 ✅

如仍有问题,建议使用 mosquitto_sub 或 MQTTX 工具分别监听原始主题和日志主题,验证消息流向。

标签: MQTT

仅有一条评论

  1. 冷秋 冷秋

    实用

添加新评论