Mosquitto 1.1 版本特性与修复全解析:$SYS 指标演进、SSL 内存优化与客户端库重构
2026/9/24 11:27:26 网站建设 项目流程
  • 后端
  • 消息队列
  • 消息路由

【免费下载链接】mosquitto

Eclipse Mosquitto - An open source MQTT broker

项目地址:https://gitcode.com/gh_mirrors/mos/mosquitto
点击查看免费下载

Mosquitto 1.1 是 Eclipse Mosquitto 早期的一个重要功能与缺陷修复版本,在 broker 监控指标、SSL 内存占用、消息去重控制、客户端库易用性等多个方向引入了影响深远的改动。本文以官方发布说明为核心,逐条拆解这些变更背后的设计意图,并结合当前仓库中的源码实现(src/sys_tree.c、src/database.c、lib/loop.c 等)给出可验证的佐证,帮助读者理解这些老特性在现代 Mosquitto 中的延续形态与底层原理。

版本定位:功能与缺陷修复并存

Mosquitto 1.1 发布于 2012 年 12 月(见 www/posts/2012/12/version-1-1-released.md),官方将其定性为 "a feature and bugfix release"。它没有引入协议层面的革命性变化,而是围绕监控可观测性内存与安全性配置灵活性客户端开发体验四个方向做了一次系统性打磨。版本内容分为 Broker、Client library、Clients、Other 四个部分,本文沿用这一脉络展开。

Broker 监控体系:$SYS 指标的演进

1.1 版本对 broker 的 $SYS 监控树做了三处关键调整,这些主题在现代版本中依旧存在于 src/sys_tree.c 的指标定义表中。

新增丢弃消息与过期会话计数器

发布说明新增了$SYS/broker/messages/dropped$SYS/broker/clients/expired两个计数器:

  • $SYS/broker/messages/dropped:统计因队列已满等原因被丢弃的 PUBLISH 消息。在当前源码中该指标对应$SYS/broker/publish/messages/dropped(src/sys_tree.c#L85),在 src/database.c 的三处消息插入路径上通过metrics__int_inc(mosq_counter_mqtt_publish_dropped, 1)递增(src/database.c#L499、L637、L644)。可以看到,"丢弃"主要发生在两种场景:客户端在线但max_inflight_messages/max_queued_messages限额已满,或客户端离线且队列容量耗尽。
  • $SYS/broker/clients/expired:统计因persistent_client_expiration机制而过期的持久会话客户端数量。当前源码中对应mosq_counter_clients_expired指标(src/sys_tree.c#L59),在 src/session_expiry.c#L209 的会话过期清理逻辑中递增。

这两项指标让运维人员可以直接通过订阅 $SYS 主题观察"消息是否在丢"和"持久会话是否被清理",而无需依赖日志。

负载指标从瞬时速率改为移动平均

发布说明中一项重要的监控语义变更:将原先$SYS/broker/+/per second/+这类瞬时速率指标,替换为发布在$SYS/broker/load/#下的移动平均值(moving average),并新增$SYS/broker/load/sockets/+$SYS/broker/load/connections/+

从当前源码可以清晰看到这套机制的最终形态:src/sys_tree.c#L114-L142 中定义了struct metric_load数组,为每个被监控的计数器(消息收发、发布收发、字节收发、socket 连接数、MQTT CONNECT 次数)分别维护 1 分钟、5 分钟、15 分钟三个时间窗的负载指标,例如:

$SYS/broker/load/messages/received/1min $SYS/broker/load/messages/received/5min $SYS/broker/load/messages/received/15min $SYS/broker/load/sockets/1min $SYS/broker/load/sockets/5min $SYS/broker/load/sockets/15min $SYS/broker/load/connections/1min $SYS/broker/load/connections/5min $SYS/broker/load/connections/15min

其中 socket 负载基于mosq_counter_socket_connections、连接负载基于mosq_counter_mqtt_connect_received。移动平均相比瞬时值能更平滑地反映趋势,避免单秒突发流量造成监控告警抖动,也奠定了后续所有 Mosquitto 版本负载监控的基本范式。

堆内存指标的字符串格式调整

发布说明提到$SYS/broker/heap/#消息不再在数值字符串中附带 "bytes" 字样,方便直接解析使用。这一改动体现了早期版本对监控数据"机器可读性"的重视——纯数值输出使订阅端无需再做字符串截取即可做数值比较或入库。

SSL 内存优化与 CRIME 攻击防护

发布说明在 Broker 与客户端库两部分都提到了两条相同的 SSL 变更:

  1. 禁用 SSL 压缩(Disable SSL compression):既能显著降低内存占用,又消除了CRIME 类攻击(通过压缩比率推断明文内容的侧信道攻击)的可能性。对于 MQTT 这种大量小报文、明文可预测性高的场景,SSL 压缩收益本就可忽略,禁用是安全与性能上的正确取舍。
  2. 启用SSL_MODE_RELEASE_BUFFERS:让 OpenSSL 在握手完成后释放读/写缓冲,进一步压缩每个 TLS 连接的内存开销。

这两项改动尤其适合大量短连接或大规模设备接入的场景——每个连接的 SSL 缓冲区都会产生可观的内存累积,而 MQTT 物联网场景恰恰是海量轻连接的代表。

新增 allow_duplicate_messages 配置项

1.1 引入的allow_duplicate_messages选项控制 broker 是否向同一客户端重复投递相同消息。在当前源码中:

  • 默认值为true(src/conf.c#L287),保持早期版本的兼容行为;
  • 配置解析位于 src/conf.c#L1123-L1125,接受布尔值;
  • 实际生效逻辑在 src/database.c#L586-L596:当allow_duplicate_messages == false且消息非 retained、非 MQTT v5 协议时,broker 会检查base_msg->dest_ids中是否已记录该客户端 ID,若已投递过则直接跳过。

从实现可以推断,该选项的价值在于:当客户端对同一主题有多个重叠订阅(例如同时订阅a/+a/b)时,broker 默认会对匹配的每条订阅各投递一次;关闭该选项可以避免这种重复投递,减少带宽与客户端处理负担。注意该逻辑对 MQTT v5 不生效,这是协议语义演进的体现。

ACL 文件支持 # 注释行

发布说明规定 ACL 文件可以用#作为行首字符编写注释。当前实现位于 plugins/acl-file/acl_parse.c#L214-L222:解析循环中对每行去除末尾空白后,if(buf[0] == '#'){ continue; }直接跳过注释行。这让 ACL 文件可以附带策略说明,提升可维护性。与此类似的注释约定也可见于 aclfile 示例(仓库根目录 aclfile.example)。

启动日志与配置加载可见性

发布说明提到启动时显示正在加载的配置文件。当前源码在 src/conf.c#L1876 中通过log__printf(NULL, MOSQ_LOG_INFO, "Loading config file '%s'", files[i])输出,且支持include_dir时逐个打印;同时-c参数与--test-config(src/conf.c#L561)配合,可以让运维明确知道实际生效的配置来源,便于排查"改了配置没生效"类问题。

消息流控制修复与桥接行为修正

max_inflight_messages / max_queued_messages 生效修复

1.1 修复了max_inflight_messagesmax_queued_messages未正确生效的缺陷。这两个参数在 mosquitto.conf 中有明确说明:

  • max_inflight_messages:每个客户端在途(inflight)的 QoS 1/2 消息上限,默认 20,设为 0 表示无上限,设为 1 可保证 QoS 1/2 消息严格按序投递(mosquitto.conf#L86-L92);
  • max_queued_messages:超出在途额度后排队等候的 QoS 1/2 消息上限,默认 1000(mosquitto.conf#L140-L145);
  • 与之配套的还有按字节计量的max_inflight_bytes/max_queued_bytes(mosquitto.conf#L81-L84、L129-L134),当两种限额同时指定时按先达到者生效。

结合 src/database.c 的插入路径可以看出这两个参数的实际作用点:客户端在线时优先进入在途发送(db__ready_for_flight),满额后转入排队(db__ready_for_queue),两者都满则触发丢弃计数并记录 "Outgoing messages are being dropped" 日志(src/database.c#L630-L638)。

QoS 2 消息及时投递

发布说明确保 QoS 2 排队消息能及时发送。QoS 2 涉及 PUBREC/PUBREL 四次握手,消息可能长时间滞留在半途状态;该修复属于消息状态机的时序调整,保证在途窗口释放后排队消息能够尽快进入发送流程。

本地桥接的 clean_session 行为

1.1 修复了两处本地桥接(local bridge)问题:

  • 本地桥接现在能正确处理clean_session
  • clean_session false的本地桥接在 broker 重启后会清理不再使用的订阅。

这保证了桥接会话在持久化模式下重启后不会残留陈旧订阅,避免订阅关系与实际业务脱节。桥接相关逻辑可在 src/bridge.c 中继续追踪。

客户端库:易用性与健壮性重构

循环 API 的职责重新划分

1.1 将错误处理从mosquitto_loop()下沉到mosquitto_loop_read()/mosquitto_loop_write(),使这两个函数在独立事件循环(如 select/poll/epoll 封装)中使用时行为更直接。同时新增mosquitto_loop_forever()帮助简化客户端主循环——当前实现在 lib/loop.c#L244 起:内部以while(mosq->run)反复调用mosquitto_loop(),遇到致命错误(MOSQ_ERR_NOMEMMOSQ_ERR_PROTOCOLMOSQ_ERR_TLS等)才退出,普通错误则走自动重连逻辑。C++ 绑定mosquittopp也同步提供了loop_forever()封装(lib/cpp/mosquittopp.cpp#L524)。

此外:

  • mosquitto_loop()在系统调用被信号中断(EINTR)时不再返回错误,避免线程模式下不必要的断连/重连;
  • 线程模式忽略 SIGPIPE,防止意外终止客户端进程;
  • 网络读写前重置 errno,避免 EAGAIN 被误判返回。

消息队列长度取代 max_packets

发布说明指出:消息队列长度现在被跟踪,并用来决定单次最多处理多少个数据包,max_packets参数因此变得无用。这一改动让库能够根据实际积压自适应批处理,而不是依赖调用者拍脑袋指定包数。

TLS 配置的即时校验

mosquitto_tls_set()现在会立即校验 CA 证书或客户端证书/密钥是否可访问,失败时马上返回错误(C 库)或抛异常(其他语言绑定),而不是等到连接阶段才暴露问题,大幅缩短了配置错误的排查周期。

API 命名与语义修正

  • 将所有obj参数统一重命名为userdata,与生态内其他库保持一致,也减少了后续文档与示例的歧义;
  • 修复mosquitto_topic_matches_sub()+结尾的订阅(如a/+)的匹配错误(bug #1085797);
  • 修复 Python 绑定error_string()的错误返回值(bug #1086777)以及 PINGREQ 发送后未重置收发计时器导致过早断开的问题;
  • 修复mosquitto_message_retry_set()的文档错误;
  • 修复连接失败时的潜在内存泄漏;
  • mosquitto_lib_cleanup()尽可能释放 OpenSSL 占用的内存(当前实现见 lib/libmosquitto.c#L80)。

WebSocket 子协议命名

客户端库将 WebSocket 子协议名改为mqttv3.1,便于未来演进并兼容其他实现。这为后续支持 MQTT v3.1.1/v5 的 WebSocket 连接预留了清晰命名空间。

命令行客户端改进

  • mosquitto_pub/mosquitto_sub的 help 消息现在同时显示自身版本号和所链接库的版本号,便于快速核对二进制与库是否匹配;
  • 修复mosquitto_pub -l -q 2(从标准输入逐行发布、QoS 2)在消息全部发送前就断开连接的问题;
  • 修复客户端 ID 处理中的潜在数组越界访问(bug #1083182)。

其他工具:mosquitto_passwd 支持明文转哈希

发布说明最后一条提及mosquitto_passwd可将纯文本密码文件转换为哈希版本。当前实现位于 apps/mosquitto_passwd/mosquitto_passwd.c:

  • -U选项:"update a plain text password file to use hashed passwords"(apps/mosquitto_passwd/mosquitto_passwd.c#L125);
  • 核心逻辑update_file()通过pwfile_iterate()逐行读取旧文件,将明文密码替换为哈希后写回(apps/mosquitto_passwd/mosquitto_passwd.c#L281-L283);
  • 同时支持只更新指定用户的-U <username>形式(update_pwuser(),见 apps/mosquitto_passwd/mosquitto_passwd.c#L290-L318)。

该功能让历史遗留的明文密码文件可以在不重建用户体系的前提下平滑迁移到安全哈希格式。仓库根目录的 pwfile.example 展示了哈希密码文件的格式样例。

总结与验证建议

Mosquitto 1.1 的变更虽然距今已久,但它确立的几项设计至今仍是 Mosquitto 的基石:$SYS 负载移动平均指标体系(src/sys_tree.c)、allow_duplicate_messages 去重控制(src/database.c)、SSL_MODE_RELEASE_BUFFERS 内存策略mosquitto_loop_forever 循环模型(lib/loop.c)以及mosquitto_passwd -U 密码迁移(apps/mosquitto_passwd/mosquitto_passwd.c)。

读者可以在当前仓库中直接验证这些特性:订阅$SYS/broker/load/#观察移动平均指标,在 mosquitto.conf 中配置allow_duplicate_messages false观察重叠订阅的投递行为,或执行mosquitto_passwd -U pwfile完成明文密码文件升级。本文所有源码引用均可在仓库对应路径下核对,发布说明原文见 www/posts/2012/12/version-1-1-released.md。

  • 后端
  • 消息队列
  • 消息路由

【免费下载链接】mosquitto

Eclipse Mosquitto - An open source MQTT broker

项目地址:https://gitcode.com/gh_mirrors/mos/mosquitto
点击查看免费下载

创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询