2. MQTT协议核心机制与Mosquitto架构

MQTT 之所以适合嵌入式场景,核心在于它把“谁发给谁”这件事从点对点连接中解耦出来,交给一个中心化的 broker 来撮合。理解 Mosquitto,必须先理解它实现的这套协议机制:发布订阅模型、主题与通配符、QoS 等级、保留消息、遗嘱消息和会话状态。这些机制不是孤立特性,而是共同决定了消息能否可靠、及时、按预期送达。

发布订阅模型:解耦的三角关系

在 MQTT 中,消息的流动不发生在两个客户端之间,而是经过 broker 中转。发布者(Publisher)只负责把消息发到某个主题,订阅者(Subscriber)只负责声明自己关心哪些主题,broker 负责把两者匹配起来。发布者不需要知道订阅者的 IP、端口或数量,订阅者也不需要知道消息由谁产生。这种解耦带来三个直接好处:设备可以随时上下线而不影响对方;一个传感器数据可以被多个消费者同时使用;系统扩容时只需增加订阅者,不必修改发布端。

以温度采集为例,节点 A 向 home/room1/temp 发布 23.5,节点 B 和节点 C 都订阅了该主题,broker 会把同一条消息分别投递给 B 和 C。若此时 D 也订阅了 home/+/temp,它同样会收到。发布者与订阅者之间没有直接连接,全部依赖 broker 的主题匹配表。

主题与通配符:层级化的寻址方式

主题是 UTF-8 字符串,用斜杠 / 分层,例如 factory/line1/motor/temperature。层级设计让主题本身具备语义,便于按设备、区域、测点组织。通配符有两种:+ 匹配单层,# 匹配多层且只能出现在末尾。factory/+/temperature 能匹配 factory/line1/temperature 和 factory/line2/temperature,但不会匹配 factory/line1/motor/temperature;factory/# 则能匹配 factory 下所有层级。

需要特别注意:以 $ 开头的主题(如 $SYS/broker/clients/connected)是 broker 内部统计主题,普通通配符 # 不会匹配它们,必须显式订阅 $SYS/#。另外,主题名区分大小写,Temp 与 temp 是两个不同主题,嵌入式固件中应统一命名规范,避免因大小写不一致导致订阅失效。

QoS 等级:可靠性与开销的权衡

QoS 决定消息投递的保证程度,共三级。QoS 0 是“最多一次”,发布后不等待确认,适合高频、可容忍丢包的场景,如周期性状态广播。QoS 1 是“至少一次”,接收方回 PUBACK,若未收到会重发,因此可能重复,适合大多数控制指令。QoS 2 是“恰好一次”,通过 PUBREC、PUBREL、PUBCOMP 四次握手完成,开销最大,适合计费、开关动作等不允许重复的场合。

QoS 是发布与订阅两端协商的结果:实际投递等级取发布 QoS 与订阅 QoS 的较小值。例如发布用 QoS 2、订阅用 QoS 1,最终按 QoS 1 投递。嵌入式设备资源有限,通常对遥测数据用 QoS 0 或 1,对关键命令用 QoS 1,只有在极少数场景才使用 QoS 2。

保留消息、遗嘱消息与会话状态

保留消息(Retained Message)让 broker 为某个主题保存最后一条消息。新订阅者一旦订阅该主题,立即收到这条保留消息,而不必等待下一次发布。它适合发布设备当前状态,例如 device/01/status 的 online。注意:向同一主题发布新的保留消息会覆盖旧的;发布空载荷的保留消息可清除该主题的保留值。

遗嘱消息(Will Message)在客户端异常断开时由 broker 代为发布。客户端连接时通过 CONNECT 报文携带遗嘱主题、载荷、QoS 和 retain 标志。若客户端正常发送 DISCONNECT 断开,遗嘱不会触发;若因网络中断、掉电或心跳超时被 broker 判定离线,遗嘱立即发布。典型用法是设备上线发布 online,遗嘱设为 offline,让监控端及时感知掉线。

会话状态(Session State)决定客户端断开后 broker 是否保留其订阅和未确认消息。CONNECT 报文中 Clean Session 为 1 时,每次连接都是全新会话,断开即清除;为 0 时,broker 保留会话,客户端重连后继续接收离线期间排队的 QoS 1/2 消息。嵌入式设备若需要断线续传,应使用持久会话并配合固定 Client ID;若每次都是临时采集,则用 Clean Session 减少 broker 负担。

MQTT 发布订阅核心机制与 Mosquitto 组成 Mosquitto Broker 主题匹配表 / 订阅树 QoS 会话队列 保留消息存储 遗嘱消息触发 $SYS 统计主题 发布者 Publisher 传感器节点 A PUBLISH home/room1/temp 订阅者 B SUB home/room1/temp QoS 1 订阅者 C SUB home/+/temp QoS 0 桥接 Bridge 远程 Broker topic 前缀映射 PUBLISH 投递 QoS 1 投递 QoS 0 转发 保留消息 Retained 新订阅者立即收到最后状态 device/01/status = online 遗嘱消息 Will 异常断开时 broker 代发 device/01/status = offline 会话状态 Session Clean Session=0 保留订阅 离线 QoS1/2 消息排队

Mosquitto 的组成:broker、客户端库与桥接

Mosquitto 项目本身由三部分构成。核心是 mosquitto broker 进程,负责监听端口、维护订阅树、处理 QoS 握手、存储保留消息和会话。默认监听 1883(明文)和 8883(TLS),配置文件通常为 /etc/mosquitto/mosquitto.conf。第二部分是客户端库,提供 libmosquitto 以及 mosquitto_pub、mosquitto_sub 命令行工具,嵌入式 C 程序通常链接 libmosquitto 完成连接、发布和订阅。第三部分是桥接模块(Bridge),允许一个 Mosquitto 实例作为客户端连接到另一个 broker,实现跨网络、跨云的消息转发。

桥接的典型配置如下,它把本地 factory/# 主题转发到远程 broker,并指定远程地址与主题前缀:

# mosquitto.conf 桥接示例
connection bridge-to-cloud
address cloud.example.com:1883
topic factory/# out 1
topic cloud/cmd/# in 1
remote_clientid edge-gw-01
cleansession false

其中 out 表示本地消息发往远程,in 表示从远程接收,数字为 QoS。桥接让边缘网关无需额外程序即可完成数据上云和指令下发,是嵌入式 MQTT 系统常见的拓扑扩展方式。

调试建议:用 mosquitto_sub -h localhost -t '$SYS/#' -v 可观察 broker 的客户端连接数、消息吞吐等运行指标;用 mosquitto_sub -t 'home/#' -v 配合 mosquitto_pub 可快速验证主题匹配与 QoS 行为是否符合预期。

把上述机制串起来看:主题决定消息去哪,QoS 决定消息多可靠,保留消息解决“新订阅者看不到历史状态”,遗嘱消息解决“设备掉线如何被发现”,会话状态解决“断线期间消息是否丢失”,桥接解决“多个 broker 如何协同”。Mosquitto 正是把这些机制实现为一个轻量、可配置、可嵌入的 broker,使嵌入式设备能够以很小的资源代价接入完整的 MQTT 消息体系。