前两个月我接手了一个充电桩平台的项目,法务突然跑过来说要搞一轮开源合规审计,让我把项目里依赖的 MQTT 协议栈清单全部梳理一遍,尤其是直接用到的 Mosquitto 要不要替换、能不能继续用。这一下就把我拉回到了一个老问题上:MQTT 生态里,究竟该选谁来做消息中间件?尤其是在国内团队主导的项目里,“用国产的替换掉 Mosquitto / EMQX” 这个话题几乎每次技术评审都会被翻出来。
这篇文章我不打算写那种罗列式对比,而是结合我自己做过的实际迁移和压测经历,聊聊 MQTT 协议栈选型背后的版权逻辑、商用风险,以及一套可以照着做的替换评估流程。核心就三件事:为什么要换、换的时候怎么验、踩过哪些坑。无论你是在评估企业级物联网平台,还是只在自己服务器上搭过几个 broker,这篇文章应该都能给你一些参考。
1. 先搞清楚“替代”到底替代的是什么
很多团队把“替代 MQTT 协议栈”直接等同于“换一个 broker 程序”,其实只对了一半。MQTT 这套东西从应用层拆开看,至少包含服务端 broker、客户端 SDK、协议解析与连接管理几层。你在生产环境里说“替换”,通常意味着整个消息链路都要动,只是边界有大有小。
1.1 消息链路里 MQTT broker 扮演什么角色
MQTT 的通信模型是发布订阅。设备端作为 publisher 把数据发到某个 topic,订阅方通过 broker 收到消息,设备与设备之间不直连。这个设计让 IoT 场景里的弱网设备、大量长连接、异步消息传递变得可控。broker 就是整条链路的心脏,负责维护连接、管理 session、按 topic 做消息路由、处理遗嘱消息和保留消息。
如果项目只是几十个设备、每天几千条消息,那选谁都不难,随便一个单机 broker 都能扛。可一旦规模上来,比如几万设备同时在线、消息吞吐每秒上万条,broker 的选型就直接决定了你后面会不会半夜被报警电话吵醒。这也是为什么我在评估替代方案时,第一件事不是看协议栈用了什么语言写的,而是先看它能不能扛住业务实际的压力模型。
1.2 Mosquitto 和 EMQX 为什么会成为“标配”
Mosquitto 是 Eclipse 基金会下面的项目,C 语言实现,轻量、稳定,特别适合跑在网关、智能设备、或者小规模服务器上。我早期用它做过很多边缘采集项目,SDK 文档少但胜在简单,配置起来半小时的事。EMQX 则走了完全不同的路线,Erlang/OTP 编写,天然擅长高并发连接管理,还自带规则引擎、数据集成、集群能力,几乎成了国内 IoT 平台的默认首选。
需要注意的是,这俩虽然都是“开源”,但开源许可证差别不小。很多团队一直把“免费拉到代码”等同于“可以任意商用”,这个认知在真正做合规审计时是要付出代价的。后面我会单独把许可证这一块拆开讲,因为这就是大部分替换需求的根源。
1.3 排除“整数替换”思维:技术形态差异很大
还有一个常见的误区,是认为“只要实现了 MQTT 协议的 broker 就能无缝替换另一个”。理论上协议一样,客户端连谁都能发消息;但实际工程里,替换的难点从来不在协议那层,而在生态和运维习惯上。比如 EMQX 的 Dashboard、规则引擎、WebHook 插件、与数据库的集成,这些都让开发团队形成了依赖。换掉 broker 的同时,也等于换掉这些配套能力,重构工作量一不小心就超标。
所以我更愿意把“替代”理解为一次完整的技术评估和迁移项目,而不是简单的服务替换。下面从版权、技术能力、实操迁移三个角度逐步拆。
2. 开源许可证与商用风险,是替换的第一动力
说句实话,很多团队做协议栈替换,不是嫌弃人家功能不行,而是法务和老板在台上说“这个授权有问题,不能用”。所以在谈技术选型之前,最好先把许可证这条线捋清楚。
2.1 Mosquitto 采用的 EPL 许可证到底限制什么
Mosquitto 的主要许可证是 EPL-2.0(Eclipse Public License 2.0),部分组件也有双许可或 BSD 式选项。EPL 是一个偏“弱 copyleft”的许可证,核心要理解一点:如果你只是把 Mosquitto 拿过来作为独立程序运行,通过 MQTT 协议和它通信,那么你的业务代码、客户端代码都不构成“衍生作品”,不受开源传染约束。这点和很多网络服务型软件不一样,协议边界就是代码边界。
真正的风险点在于:如果你们团队自己改了 Mosquitto 源码,再把修改后的版本对外分发,那修改部分的源码就必须继续以 EPL 方式开源。还有,如果你们把修改版作为一个组件嵌入到自己的商业产品里一起发布,也容易触发衍生作品认定。企业号项目里最怕这种情况,因为产品对外卖的是整套软硬件,而里面某个隐含组件可能已经“被开源”了。
这就是为什么很多聚焦交付一体机的团队,最终毅然选择换掉 Mosquitto。他们不是用不起,而是没办法向客户承诺“软件供应链完全合规、不会被要求开源商业代码”。这种风险你要是提前不知道,等到合同审计或者被人举报的那天,就非常被动了。
2.2 EMQX 的 Apache-2.0 与开源版/企业版割裂
EMQX 的许可证设计比 Mosquitto 更宽松。它的开源版使用 Apache-2.0,这个许可证允许自由使用、修改、商用,只要保留版权声明和修改说明即可,没有 copyleft 要求。所以单纯从版权风险角度讲,EMQX 开源版比 Mosquitto 安全得多,也是为什么很多商业团队敢直接拿 EMQX 开源版做底层。
但 EMQX 的真正商业风险不在许可证,而在产品版。它把很多高价值能力,比如大规模集群管理、K8s Operator、企业级数据集成、多活容灾等,都放进了闭源的企业版。开源版想通过集群扩展横向撑高并发,或者想用现成的规则引擎对接各类数据库,都受到明显限制。你要是照着网上教程和社区文档把开源版搭起来,业务到了某个量级之后会发现,路越走越窄,要么自己造轮子,要么掏钱买企业版授权。
另外商标层面也要注意:Apache-2.0 授权的是代码,不授权项目商标。如果你的商业产品名字里带 “EMQX” 字样,或者对外宣传“内置 EMQX”,这属于商标使用,需要遵循官方商标政策,不能因为代码是开源的就想怎么用怎么用。
2.3 授权模式与商用风险对照表
为了不绕晕,我把几个常见选择的授权和风险点集中在一个表里。
| 方案 | 许可证 | 修改后分发要求 | 商用风险 | 主要适用场景 |
|---|---|---|---|---|
| Mosquitto | EPL-2.0(部分组件双许可) | 修改版以 EPL 开源 | 嵌入式/一体机发售时,容易触发衍生作品争议 | 边缘网关、小规模服务 |
| EMQX 开源版 | Apache-2.0 | 保留声明,无强制开源 | 企业能力缺失,需要自运维补齐;商标使用受限 | 中型平台、高并发场景 |
| EMQX 企业版 | 商业许可证(闭源) | 按合同执行 | 付费成本,授权粒度复杂 | 大规模集群、官方支持 |
| 国内团队自研 broker | 常见商业/定制授权或 Apache-2.0 | 视具体项目而定 | 需要自己做代码审查和技术背书 | 信创项目、定制化需求 |
这张表本质上是提醒大家:不要把“开源”和“安全商用”这两个概念画等号。开源只是在代码可访问性上给出了承诺,但要保证商业安全,你得逐字看许可证条款,甚至请法务做一次整体评估。
2.4 真正的“国产替代”到底替代了什么
这里要澄清一个说法。“国产 MQTT 协议栈”我理解的含义,是行情内部有国内团队深度维护、代码可控的 MQTT broker 实现及配套客户端 SDK,不依赖国外基金会或商业公司的单方面控制。选这类方案的人,看重的不一定是代码完全从零写的,而是“出了问题找得到人,想改功能提得了需求”。
有个很现实的问题:开源项目看起来免费,但一旦你的业务高度依赖它的修复节奏,创始人维护意愿一变,升级路径立刻中断。我见过有个团队一直卡在 Mosquitto 某个旧版本,因为新版本改了一个配置行为,导致他们的设备固件启动变慢,又不想花钱找商业支持,最后只能自己维护分支,白白浪费大量人力。这种隐性成本,比许可证风险更隐蔽,也更致命。
所以替换时,我会建议把“自主灵活性”和“供应链稳定性”也算进投入产出比里。国产方案如果只是一个“能连上、能收发消息”的demo,不够;它得证明自己能长期演进出你想要的东西,并且出了问题有人响应。
3. 技术评估维度:不止打开连接发个消息
确定了要替换,也不能闭着眼睛选。我总结了四个必须带着团队逐个核对的维度,缺一个,后面上线都得补课。
3.1 基础协议能力:MQTT 3.1.1 的细节最容易藏雷
MQTT 3.1.1 是当前应用最广的版本,但它并不简单。QoS 0、1、2 三种质量等级,行为完全不同。QoS 1 至少一次,有重复投递的可能;QoS 2 恰好一次,需要四步握手,性能开销最大,但消息不会重复。很多国产方案开发时为了省事,把 QoS 1 和 QoS 2 的实现做得模模糊糊,比如把 QoS 2 直接降级成 QoS 1,这会在实际业务里造成重复消息或者消息状态错乱。
我在验证候选协议栈的时候,会专门写一批用例去测这些场景:客户端断线重连之后,未确认的 QoS 1 消息会不会重新推送;遗嘱消息在异常断网和正常断开时分别怎么触发;保留消息在不同 session 场景下的清理逻辑;cleanSession 从 0 切到 1 时,服务器会不会把旧 session 数据清理干净。每一项都有对应的标准行为,拿测试结果逐个比对,才敢进压测环节。
3.2 MQTT 5.0 新特性覆盖:不能只要 3.1.1
如果你所在的技术选型是在 2023 年之后做的,我强烈建议把 MQTT 5.0 支持度列为硬性指标。5.0 不是 3.1.1 的小升级,它引入了很多真正有用的机制:主题别名能大幅减少报文体积,对窄带设备是实打实的优化;共享订阅解决了多个消费者负载均衡的问题;请求/响应模式让远程调用语义更清晰;用户属性则把自定义扩展信息塞进报文头,省去了在 payload 里做二次封装的麻烦。
不少国产协议栈迭代慢,到现在还只支持 3.1.1,对外宣传却说“兼容 MQTT 协议”。你如果在评估时不多问一句“5.0 支持到什么程度”,等设备端用上了 5.0 特性,服务端直接无法处理,来回排查会浪费几个星期。所以我会把 5.0 的兼容清单直接写进技术评审表里,逐条打勾。
3.3 性能与稳定性:连接数、吞吐、延迟各看各的
性能评估里最容易犯的错,是只盯着“最大并发连接数”一个指标。真实的物联网流量模型里,连接数、消息吞吐、端到端延迟是三个独立的维度。有的协议栈可以扛住十万连接,但一旦每个连接都高频发消息,处理线程就全部阻塞;有的吞吐测试看起来很高,但消息在 broker 内部积压,端到端延迟早就超过了业务容忍线。
压测的做法我会在第 4 节详细展开,这里先提醒:压测工具和脚本要自己控,不要拿厂商提供的测试报告当依据。厂商通常只报峰值,不报中位数延迟和长尾延迟,也不告诉你它用了多大规模的压测机。真正的技术负责人要自己定义场景:多少连接、每秒多少条消息、payload 多大、发生多少订阅关系、是否混合不同 QoS。这些参数不同,结果能差出好几倍。
3.4 可观测性与运维生态:上线之后才知道疼
一个消息中间件选型合不合格,上线三个月后最有发言权。国产自研协议栈最容易缺的就是运维能力:监控指标不齐全,看不出当前连接数、消息积压数、订阅关系增长曲线;日志格式混乱,出问题很难定位是哪一个客户端在刷消息;没有管理端或者管理端极简,运营人员想踢掉一个异常连接都找不到入口。
我建议把“可观测性”拆成日志、指标、审计三个子项来打分。日志要能按 client ID 过滤;指标要兼容 Prometheus 格式或至少能通过 HTTP 暴露关键数值;审计要有登录、配置变更、权限变更记录,这在等保或者内部审计的时候几乎是刚需。没有这三样,任何协议栈再快你也不要把它当核心基础设施。
4. 从 Mosquitto / EMQX 迁移到国产协议栈的实操路径
替换不是直接换一个二进制文件。我把它拆成了四个阶段:先摸清现状,再做兼容性验证,接着压测,最后灰度切换。每一步都有明确的收尾标志。
4.1 迁移前:把客户端特性清单列出来
第一步是给现有系统做“协议画像”。把所有接入 MQTT 的服务、设备端 SDK、脚本工具都翻出来,列一份表格,记录它们用到了哪些特性:用的 MQTT 版本,QoS 等级哪些,有没有遗嘱,有没有保留消息,有没有用共享订阅,有没有用到 topic 前缀的约定,有没有用到 MQTT 5.0 的某种特殊能力。
这一步看起来繁琐,但能挡住 80% 的迁移故障。我之前在一个项目里做评估,第一版清单只写了“客户端全用 QoS 1”,结果深挖之后发现设备固件里其实用了 Qos 2 的 retained 消息,而且发送频率还不低。如果真按 QoS 1 去压测,上线后会出现大量重复消息,整个事件回溯都要重做。
4.2 协议兼容性测试:用最笨但最有效的方法
这一步我推荐直接搭一套模拟环境,把新旧两套 broker 并行跑起来,再用同一批测试脚本分别连上去,对比行为差异。工具上,mosquitto_pub和mosquitto_sub足够做基本收发验证;更复杂的订阅场景建议用 Python 的 paho-mqtt 库写脚本,因为它支持很多底层参数设置,能精确模拟设备端行为。
测试用例至少要覆盖:正常收发、QoS 1/2 的断线重传、遗嘱触发、保留消息写入与清除、session 恢复、共享订阅负载均衡、topic 通配符订阅。你可以把测试结果写成表格,新旧两侧打勾,任何一项对不上都要去找厂商或者原厂技术确认,不要抱有“细节不重要”的侥幸心态。
4.3 性能压测:压出真实问题的四组参数
压测阶段,我会固定下四组核心参数:并发连接数、发布速率、订阅关系数、消息 payload 大小。比如先做一万个连接、每个连接每秒发一条 QoS 1 消息、payload 256 字节的基准场景;然后再逐步提高发布速率,观察 broker 的 CPU、内存和 GC 情况。
压测工具方面,EMQ 的emqtt-bench挺好用,能模拟大量连接和消息发布;如果目标 broker 是一个小众的国产方案,最好自己也写一个基于 paho 的脚本,防止压测工具自身的特性掩盖了问题。特别要注意,emqtt-bench和某些自研 broker 可能在 MQTT 5.0 支持细节上有差异,一旦压测时发现大量连接失败,第一件事是确认压测工具用的协议版本,而不是急着怀疑 broker 不行。
压测的另一个重点是“长稳测试”。只跑十分钟看不出问题,至少要持续一到两个小时,观察内存是否持续上涨、连接是否掉线、消息是否有累计积压。很多国产协议栈在短时高并发下表现优秀,但跑半小时后内存逐渐失控,就是因为内部某个缓冲队列没有做好上限管理。
4.4 灰度切换:数据分流和回滚预案要同步做
迁移到生产环境,我是强烈不建议“停掉旧的、直接起新的”这种方案的。正确做法是让新旧 broker 并行运行,通过网关或配置中心把一小部分设备流量切到新 broker 上运行一段时间,验证稳定后,再逐步放大比例,直到全部切完。
同时一定要提前约定回滚机制。切换后一旦发现异常连接率上升、消息丢失增多或者监控指标告警,要有能力在几分钟内把流量切回旧 broker。回滚本身也不只是改个配置,要确认旧 broker 侧的 session 数据和保留消息还在,否则设备端重连后会丢失状态,引发连锁问题。我会在架构里让新旧两侧的客户端配置中心支持动态切换,操作一次只有几秒钟的 TCP 重连延迟,业务基本无感。
5. 常见问题与避坑实录
最后一部分分享一些实际会遇到的问题,以及我处理它们时的思路。每一条都是项目里真实踩过的,不写理论。
5.1 客户端连接 “握手失败” 先查协议版本
迁移国产协议栈后,最常见的第一类故障报告是“设备连不上 broker”。排查时先不要怀疑设备,直接抓包或者看 broker 端的连接日志,确认客户端发起的 CONNECT 报文使用的是 MQTT 3.1.1 还是 5.0。老设备固件很多还在用 3.1,而部分国产 broker 对新旧版本支持有选择,握手阶段如果协议版本不匹配,连接会被直接拒绝。看起来像个 bug,其实是对版本支持不完整。
我建议在新 broker 入口做一层协议版本记录,把每次连接的 protocol version 打点上报。这样一旦有兼容性问题,运维可以根据 client ID 快速定位设备固件版本,再决定是升级设备还是调整 broker 侧兼容模式。
5.2 消息重复不是 broker 的问题,但你要设计幂等
MQTT 本身在 QoS 1 和部分网络重传场景下就允许消息重复,这不是国产协议栈独有的毛病。很多团队迁移后才发现旧 broker 通过某种机制“掩盖”了部分重复,而新协议栈行为更标准,于是重复率数字立刻上去了。这个概率很高,最好提前想好应对:消费者侧按消息 ID 做去重,或者设计成天然幂等。
我在一个设备控制项目里就吃过这个亏。迁移前设备的命令下发大概每千条才出现一两条重复,大家都没在意;迁移后因为 session 清理策略差异,重复率翻了几倍,设备偶尔收到同一条命令执行两次。后来我在消费逻辑里加入了消息去重表,以消息 ID 为唯一键,简单粗暴地解决了。
5.3 压测跑满并发但延迟很高:检查订阅匹配效率和 TCP backlog
延迟高的原因通常分两类。一是订阅树匹配效率不行,topic 层级深、通配符多的时候,自研 broker 会变成线性扫表,延迟自然上不去。二是操作系统网络参数没调,比如 TCP backlog 太小、连接队列溢出、文件描述符限制偏低,这些在连接数上来以后全是瓶颈。
性能压测前最好先把 host 上几个关键参数检查一遍:文件描述符上限、somaxconn、tcp_tw_reuse、是否开启 TCP_NODELAY。很多“自研协议栈性能差”的结论,最后追到底都发现是部署环境没优化,这个锅不能乱甩。
5.4 选型不追求最贵,追求匹配业务风险
这套流程走下来,我最大的体会是:没有绝对最好的 MQTT 协议栈,只有和自身业务最匹配的选择。如果你的业务是边缘采集、设备数量少、对成本敏感,Mosquitto 仍然好使;如果已经是中大规模平台,EMQX 开源版可以快速跑起业务;如果法务要求版权审计严格、或者需要深度定制和长期技术支持,那就必须认真评估国产自研方案。
判断的核心是风险大小:许可证风险、性能风险、运维风险、供应链风险,哪一项对你的业务影响最大,就先补哪一项。选型文档里把这些风险逐条列清楚,团队评审时就不会只是拍脑袋。
这次做协议栈替换的整个过程中,我最深刻的一个感受是:不要因为“大家都用”就停止审视,也不要因为“国产”两个字就自带滤镜。代码能跑起来只是开始,协议兼容性、长期维护能力、遇到问题时的响应速度,这些才是决定一个消息中间件能陪你走多远的东西。最后再分享一个小经验,在迁移周期内,让核心开发成员直接参与压测和兼容性用例编写,不要全部甩给测试同学,这个投入绝对值得,因为上线后的每一次凌晨告警,都会让你感谢当初多写的那些用例。