上个月帮一家做储能监控的客户做技术选型,他们的要求很简单:把所有链路里授权不够清晰的开源组件换掉,优先采用国内团队维护或拥有自主知识产权的实现。结果一查,发现最头疼的并不是功能,而是他们正在用的 Mosquitto 和 EMQX 开源版,在许可证和商用边界上存在大量容易被忽略的细节。于是我把国产 MQTT 协议栈的替代方案完整梳理了一遍,从版权条款、功能差异、迁移步骤到实际踩坑,整理成了这篇文章。如果你也在评估"是否能用国产 MQTT 协议栈替换 Mosquitto / EMQX",这篇内容基本能覆盖你需要的决策依据和实操路径。
1. 为什么会出现"国产替代 Mosquitto/EMQX"这种需求
1.1 一个真实的选型场景:从许可证焦虑说起
我接触过不少做物联网平台、充电桩、工业网关的团队,早期项目几乎都是直接用 Mosquitto 或者 EMQX 开源版跑起来的。原因很简单:网上教程多,Docker 拉个镜像就能起服务,客户端生态也很成熟。但到了产品要商业化交付的时候,问题就来了。
最典型的焦虑来自许可证。Mosquitto 使用的是 EPL/EDL 双许可,EPL 和 EDL 都属于 Eclipse 基金会系列,但两者的限制差别很大。如果只是内部使用或者原样分发,问题不大;可一旦你修改了 Mosquitto 源码,或者把包含 Mosquitto 代码的固件卖给客户,就必须仔细核对修改后的代码要不要按 EPL 开源。EMQX 开源版虽然是 Apache 2.0,相对宽松,但很多团队分不清"开源版"和"企业版"的边界,以为开源版可以无限白嫖所有特性,结果在集群负载、消息轨迹、数据集成等功能上发现被商业授权卡脖子。
这种不确定性在大项目里会被无限放大。客户法务会问:这个组件是哪来的?允许商业闭源集成吗?代码被修改后我们需要公开多少?如果后续项目要过等保,日志、审计、权限管理能不能满足要求?当这些问题得不到明确答复时,即使功能再强,也只能被替换。
1.2 国内 MQTT 协议栈的生态现状
很多人以为国内没有真正可用的 MQTT 协议栈,其实这是个误解。目前能看到的国产 MQTT 实现大致分为三类:
第一类是社区驱动的开源项目,比如用 Go 实现的 Gmqtt、用 Java 实现的一些消息中间件模块。这类项目的特点是协议完整度高、代码量适中,适合二次开发,但社区规模相比 Mosquitto 和 EMQX 小一些,文档和案例没那么全。
第二类是云厂商开放出来的 MQTT 网关或协议转换组件,比如阿里云微消息队列 MQTT 版的客户端接入协议,还有各类边缘网关自带的 MQTT Broker。这类实现通常深度绑定了云生态,但单独拎出来做私有化部署时,对底层的依赖会比较重。
第三类是企业自研的协议栈。不少做智慧城市、车联网、工业数采的公司,早期试过 Mosquitto,但是遇到性能瓶颈、集群能力不足或者被许可证问题戳中后,就用 C/C++ 或 Go 自研了一套 Broker,只保留最核心的 MQTT 协议处理逻辑,再配合自己的设备接入层和业务系统一起交付。
从我实际评估的经验看,第二类更适合云上托管,第三类适合定制化极强的项目,而第一类开源国产协议栈,是最贴近"替换 Mosquitto / EMQX"这个需求的替代品。需要注意的是,这里说的"替代"不是指无缝替换,而是指在许可证清晰、商用风险可控的前提下,用更符合团队掌控力的方案来满足同样的业务场景。
2. 开源许可证对比:GPL、EPL、Apache 2.0 与商用风险边界
2.1 Mosquitto 的 EPL/EDL 双许可到底怎么理解
很多开发者的第一反应是"Mosquitto 是开源免费的,随便用",这句话只说对了一半。Mosquitto 的源码托管在 Eclipse 基金会,采用双重许可:Eclipse Public License 1.0/2.0 和 Eclipse Distribution License 1.0/2.0。通常大家说"开源"主要指的是 EPL。
EPL 是一种弱 copyleft 许可证,对比 GPL 要温和一些,但依然有传染性。如果某个文件是基于 EPL 代码修改的,那么修改后的该文件必须继续以 EPL 发布;如果只是把 EPL 代码作为独立组件通过标准接口调用,比如把 Mosquitto 作为一个独立进程,通过 MQTT 协议与自己的业务系统通信,那么业务系统的代码不需要开源。很多人栽在这条边界上:他们把 Mosquitto 的源码直接改成内嵌库塞进自己的程序里,然后对外闭源分发,这种做法很可能违反 EPL。
关于 EDL,这个许可证更宽松,允许你修改后以闭源方式分发,前提是保留版权声明。不过要注意,Mosquitto 项目的某些特定文件可能同时包含在其他许可证下,具体要看每个文件的头部注释。我的建议是:只要动了源码,就把改动部分和整体分发方式交给法务审核,工程侧不要自以为是。
除了许可证本身,品牌商标也是一层隐性风险。Mosquitto 这个名称是 Eclipse 基金会的商标还是单纯的项目名,需要单独确认。商用宣传物料里如果直接写"基于 Mosquitto 构建",一般没问题;但如果把产品命名为"XX Mosquitto"或暗示自己是官方的商业化发行版,就有商标风险。
2.2 EMQX 的 Apache 2.0 开源版与商业版边界
EMQX 开源版采用 Apache License 2.0,这个许可证在商用友好度上比 EPL 高很多。基于 Apache 2.0 的项目做闭源商用,甚至将修改后的代码不公开,都是允许的,前提是保留原始版权声明和 NOTICE 文件。
但 EMQX 有一个"开放核心"模式,Apache 2.0 只覆盖开源版代码,企业版的很多能力并不在开源仓库里。具体来说,开源版包含核心的 MQTT Broker、集群、规则引擎、仪表盘和常用认证插件,基本满足中小规模场景。但企业版才有的功能,包括多活集群、全球消息轨迹追踪、大数据集成(Kafka / Pulsar / Snowflake)、向量嵌入、多租户权限体系、基于角色的访问控制等,都是闭源商业代码。如果你在开源版的界面里看到某个功能入口,点进去提示需要企业证书,那你使用该功能就必须购买商业授权。
这里特别容易踩的坑是:有人从 EMQX 开源仓库拉代码自己编译,然后从某些渠道拿到了企业版插件包,把它们拼接到一起。这种做法在授权上是不成立的,商业插件的使用必须遵守 EMQX 的商用协议。实际项目里我见过有团队把企业版镜像的内部依赖文件拷贝到开源版镜像里用,结果被 EMQX 官方扫描到并发了律师函。替换的时候,这类历史包袱要先清理干净。
2.3 国产协议栈常见的许可证策略与合规死角
国产 MQTT 协议栈的许可证策略分化很大。有的项目采用 Apache 2.0,鼓励商用和二次开发;有的采用 MPL 2.0,要求对修改过的文件开源;还有一部分商业公司的自研协议栈完全闭源,只通过 SDK 形式对外提供。选择哪个要看你对"可控"的定义。
如果追求最大商用灵活性,首选 Apache 2.0 的国产实现,和 EMQX 开源版的授权模型相同,团队没有额外学习成本。个人经验是,选型前必须先拉一遍代码仓库里所有第三方依赖的 LICENSE 文件,因为很多协议栈本身是 Apache 2.0,但捆绑的某个算法库或 TLS 库可能使用 GPL 或 AGPL,一旦集成,整个项目的分发性质就会变。
另一个合规死角是"内嵌 vs 独立进程"。很多国产协议栈给开发者提供了 Library 模式,比如 Go 的 package 或 Java 的 jar。如果你在业务代码中直接引用这个库,并把自己和库编译成同一个可执行文件,那么该开源库的许可证条款会影响整个程序的分发方式。即使项目使用的是 Apache 2.0,也要注意 NOTICE 文件是否带 extra attribution 条款。我的建议很简单:让法务在项目启动前把许可证表做出来,之后每次引入新依赖都走一遍这个表,不要每次都是"先上车后补票"。
3. 技术代差与功能对照:国产协议栈凭什么能替换
3.1 基础 MQTT 3.1.1/5.0 功能对比清单
做替换之前,先要把功能对表。MQTT 协议本身并不复杂,但如果只支持 3.1.1 而不支持 5.0,很多新特性就没法用。我建议用下面这张表来排查候选国产协议栈的能力覆盖情况:
| 功能点 | Mosquitto | EMQX 开源版 | 国产协议栈(以 Apache-2.0 实现为例) |
|---|---|---|---|
| MQTT 3.1.1 | 支持 | 支持 | 支持 |
| MQTT 5.0 | 2.x 支持 | 支持 | 多数支持,需确认 |
| QoS 0/1/2 | 支持 | 支持 | 支持 |
| Retain 消息 | 支持 | 支持 | 支持 |
| Will Message | 支持 | 支持 | 支持 |
| 共享订阅 | 支持(需要配置) | 支持 | 部分支持,需看实现 |
| 消息过期/会话过期 | MQTT5 支持 | 支持 | MQTT5 支持 |
| 主题别名 | MQTT5 | 支持 | 需确认 |
| 用户属性 | MQTT5 | 支持 | 需确认 |
| 持久化 | 内存/SQLite/插件 | 内置机制 | 差异大 |
| 权限订阅/发布 ACL | 文件/数据库插件 | Dashboard 配置 | 差异大 |
这个表的核心意义是:不要默认"实现了 MQTT 协议就等于能力一致"。我遇到过一个国产协议栈,用官方 MQTT 5.0 测试用例跑了一遍,协议解析没问题,但共享订阅只支持前缀匹配模式,而且 Group 里的会话重新平衡策略和 EMQX 完全不一样,导致一组消费者里有一个掉线后,消息在短时间内全部堆积到剩余消费者上,业务端的延迟从 200ms 飙到 5 秒。
因此,在替换前一定要把实际业务用到的 MQTT 特性全部列出来,写一个自动化回归测试套件,尤其要覆盖遗嘱消息、保留消息、QoS2 和共享订阅这四类。很多团队把设备接入跑通就觉得兼容了,结果上线后才发现断线重连场景下,遗嘱消息的被触发时机和预期不符。
3.2 桥接、集群、规则引擎与扩展插件的差异分析
除了协议本身,Mosquitto 和 EMQX 的差异很大程度上体现在外围能力。Mosquitto 的定位是轻量级单机 Broker,虽然 2.x 版本增加了桥接配置,但它的桥接只是简单的 topic 转发,不支持在桥接侧做复杂的消息清洗和路由。EMQX 开源版则强在集群和规则引擎,一个 Dashboard 就能看到节点状态、消息流入流出,还能用 SQL 语法做消息转换。
国产协议栈在这方面两极分化明显。偏"轻量替代"的实现,通常只带一个简单的 Web 管理接口,支持配置监听端口、打开匿名访问、配一下 ACL,功能上类似 Mosquitto 的增强版。偏"企业级替代"的实现,则会模仿 EMQX 的插件化架构,内置认证模块(HTTP API、MySQL、Redis、JWT)、TLS 终端、以及基于规则的转发组件。
从替换角度看,如果原系统重度使用了 EMQX 的规则引擎,那么国产协议栈必须至少能做到以下三点:
- 支持从 MQTT 主题中解析 JSON 负载,并把指定字段映射到目标主题或消息队列;
- 支持在消息进入业务系统前执行数据清洗,比如丢弃无效字段、添加设备上报时间戳;
- 支持故障降级,当目标通道不可用时,消息能缓存到本地磁盘而不是直接丢弃。
如果国产协议栈提供的是 Lua 脚本或 Go 插件,那么业务侧的迁移工作量就要重新评估。我的建议是:选择支持 HTTP Webhook 转发或标准协议(如 MQTT Bridge、Kafka Connector)的国产协议栈,这样即使上层接口有差异,也能通过额外写一层适配器解决,不必死守它的内部规则引擎。
3.3 性能与资源占用的实测数据参考
所谓"国产协议栈比 Mosquitto 性能好"不能一概而论。我做过一组不太严谨但很直观的对比测试:用 500 个模拟客户端,每个客户端每秒发布 10 条 256 字节的消息,订阅端共 1000 个连接,持续 10 分钟。
- Mosquitto 2.0.18 单机稳定,CPU 平均占用 23%,内存约 480MB(含系统缓存),消息延迟 P99 约 18ms;
- EMQX 5.x 集群单节点 CPU 平均占用 38%,内存约 1.2GB,P99 约 10ms;
- 某国产 Go 实现的 MQTT Broker 单机 CPU 平均占用 29%,内存约 760MB,P99 约 14ms。
从数字上看,国产协议栈完全可以替代。但要注意,这类压力测试只能说明在标准 TCP 信令场景下没问题,一旦涉及 TLS 加密、持久化、ACL 审计,性能数据会完全变样。尤其是很多国产 Broker 使用纯 Go 写 TLS 握手,长连接场景下 CPU 占用可能比 C 实现的 Mosquitto 高 30% 以上。因此,替换前必须按你的真实业务做压测,而不是拿别人的基准数据来拍板。
4. 从 Mosquitto 迁移到国产协议栈的实操路线
4.1 环境准备与依赖组件选型
迁移前先梳理现有资源。假设原系统是经典的 Mosquitto + 文件密码 + TLS,客户端包括 STM32 设备、Java 后端、Node-RED 和移动端。推荐步骤如下:
- 先确定国产协议栈的运行方式,优先选择 Docker 镜像或纯二进制包,避免依赖特定 Linux 发行版的编译环境;
- 准备好持久化目录。比如使用
/var/lib/mqtt作为数据目录、日志目录、配置目录,确保权限一致; - 确认运行账号是非 root 的专用账号,避免权限过大带来的安全隐患;
- 准备一份完整的旧配置清单,包括监听端口、协议版本、TLS 证书、ACL 规则、桥接地址。
在依赖组件上,我强烈建议提前保留三个工具:mosquitto_pub/mosquitto_sub用于快速验证连通性,mqtt-spy或者 MQTTX 用于多协议调试,wireshark用于抓包分析。不管协议栈怎么换,这三个工具都能帮你快速判断问题出在客户端还是服务器端。
4.2 认证鉴权与 TLS 双向加密配置示例
国产协议栈的配置文件风格差异很大,但一般都会提供类似下面的监听器配置。以某 Go 实现的 Broker 为例(配置结构类似 HiveMQ 的config.xml简化版):
listener.tcp = 1883 listener.ssl = 8883 listener.ssl.certfile = /etc/mqtt/certs/server.crt listener.ssl.keyfile = /etc/mqtt/certs/server.key listener.ssl.cacertfile = /etc/mqtt/certs/ca.crt listener.ssl.verify = true allow_anonymous = false auth.file.path = /etc/mqtt/auth/passwd acl.file.path = /etc/mqtt/auth/acl关键点是listener.ssl.verify = true表示开启双向 TLS,客户端必须携带 CA 签发的证书。很多团队从 Mosquitto 迁移后忘记打开这项,结果只做了单向 TLS,设备端身份根本不可信。Mosquitto 的配置写法如下,用于对照:
listener 8883 certfile /etc/mosquitto/certs/server.crt keyfile /etc/mosquitto/certs/server.key cafile /etc/mosquitto/certs/ca.crt require_certificate true use_identity_as_username true认证方面,如果原系统用的是 Mosquitto 的 password_file,那么新协议栈如果不支持同样的哈希算法,你需要提前把密码哈希转换为新格式。这里最容易掉链子的细节是:Mosquitto 默认使用$7$加盐格式的哈希,国产协议栈可能只支持bcrypt或PBKDF2。转换时不要把明文密码存到临时文件,推荐写个一次性脚本,从原系统读取密码,用新的哈希算法改写,再导入新认证库。
4.3 客户端兼容性与协议行为差异处理
迁移时最隐蔽的问题不是握手失败,而是"握手成功但行为异常"。我举几个真实案例。
案例一:某个使用 Paho Java 客户端的服务在连接时设置了setCleanSession(false)和setSessionExpiryInterval(3600),旧系统 Mosquitto 2.x 会尊重这个会话过期时间,但国产协议栈的实现里把sessionExpiryInterval忽略掉了,导致客户端断线后,Broker 立刻清理了会话,订阅关系全部丢失。你排查数据时看到的表象是"重启后不再收到消息",但抓包才会发现是 Broker 没有返回 Session Present。
案例二:发布者使用 QoS2,消息在完成PUBLISH后立即断连,旧系统会把 QoS2 消息持久化并在客户端重连后补发,国产协议栈因为消息 ID 的内存管理差异,可能直接把消息丢了。这类问题需要针对性压测,不能只测在线收发。
案例三:共享订阅组内的订阅者前缀过滤规则。MQTT 5.0 支持$share/g1/topic,但不同 Broker 对$share前缀之后的层级匹配策略不同。国产协议栈如果只支持精确主题匹配,不支持通配符#和+,那么共享订阅在"下行命令一对多设备"的场景会出问题。
处理这些差异的最有效方式是,在迁移前写一个基于 Paho 或 MQTTX 的场景脚本,覆盖以下场景:
- 客户端 A 上线,订阅
device/+/status; - 客户端 A 断线并设置遗嘱
device/a/status = offline; - 客户端 B 上线,向
device/a/status发布online; - 客户端 A 重连,检查是否收到遗嘱消息、是否收到离线期间保留消息、Session Present 是否为 true。
把脚本跑在旧 Mosquitto 和新协议栈上,对比结果,差异就会非常明显。
5. 商用落地中最容易踩的五个坑
5.1 许可证"传染性"被误读后的法律风险
不少开发者的理解是"只要不卖软件,许可证就管不到我",其实大错。商用并不只是"卖软件",还包括"通过软件提供收费服务"、"预装到设备内并以硬件形式销售"、"作为 SaaS 平台的基础组件对外提供服务"。EPL 和 AGPL 对 SaaS 的处理方式不同:EPL 2.0 明确规定了「向用户提供远程交互服务时,若程序以修改版形式运行,可能需要提供源代码」,这一点在很多团队的许可证评估里完全缺席。
国产协议栈如果采用了 GPL/AGPL 衍生许可证,闭源提供 MQTT 接入能力就非常危险。我们团队的做法是:法务出具一份《开源组件许可证合规清单》,每条记录包含组件名称、版本、许可证、是否修改、分发方式。任何没有列入清单的组件禁止进入生产环境。
5.2 二次开发后闭源分发:哪些代码必须开源
如果你的国产协议栈采用 Apache 2.0,那么理论上你可以把二次开发后的整个 Broker 做成闭源应用卖给最终客户,前提是保留原始版权声明和 NOTICE 文件。但这里有个隐藏陷阱:如果项目内部混用了多个许可证的开源代码,情况就复杂了。
举个例子:我们在一个网关项目里使用某国产 MQTT 协议栈,它挂在 Apache 2.0 下,但它通过 Go modules 引入了某个 MPL-2.0 授权的 WebSocket 库。MPL 2.0 对文件级别的修改有开源要求。如果我们为了修 WebSocket 并发 bug 改了那个库的源码,再把它静态编译进自己的闭源网关固件里,就可能会触发开源义务。解决方法是:尽量不要直接修改第三方依赖库,优先采用重新封装的方式,把改动隔离在自己的模块里。
5.3 日志、监控与运维可观测性的断层
功能替换也许一天就完成,但运维体系往往跟不上。很多国产协议栈的默认日志就是打 stdout,连日志轮转都没有。相比之下,Mosquitto 有log_dest file可以指定文件,EMQX 有完善的日志格式和指标接口。
在实际落地时,至少要保证新协议栈能输出结构化的 JSON 日志,并暴露以下 Prometheus 指标:
mqtt_bytes_received_total/mqtt_bytes_sent_total;mqtt_messages_received_total/mqtt_messages_sent_total;mqtt_publish_received_total;mqtt_client_connected_total/mqtt_client_disconnected_total;mqtt_session_terminated_total;mqtt_delivery_dropped_total。
如果没有这些指标,故障定位会非常痛苦。我们曾接手的项目里,国产 Broker 只提供 QPS 和连接数两个全局计数器,结果在一个垂直业务故障时,根本分不清是哪个主题的流量异常,最后只能靠抓包看端口,效率极低。建议在替换时把监控项列入验收条件,宁可晚一周上线,也要先补齐可观测性。
5.4 社区支持与 Bug 修复时效的现实差异
开源项目的生命力取决于社区活跃度。Mosquitto 和 EMQX 都有大量用户提交 issue 和 PR,Bug 修复频率高。国产协议栈,尤其是个人或小团队维护的项目,常常出现一个问题挂在 GitHub 上两三个月无人响应。商用项目中,这种不确定性会直接影响交付时间。
所以我评估一个国产协议栈时,会重点看三件事:
- 最近 6 个月的 commit 活跃度;
- 是否存在商业公司或基金会背景;
- issue 的响应速度和修复闭环比例。
如果项目只有两三个维护者,而且最近 3 个月没有新 commit,即使功能再完整,我也不建议放到核心业务链路里。毕竟 MQTT Broker 是基础设施,不是可以随便换的 UI 组件。
5.5 内网部署与等保合规的适配细节
很多物联网项目要求内部部署且通过等保测评。MQTT Broker 在其中涉及的身份鉴别、访问控制、数据完整性、日志留存等要求,都是硬指标。国产协议栈在这方面的适配往往比 Mosquitto 更灵活,因为面向国内客户需求,通常会内置国密 TLS 支持或预留加密套件扩展点。
但要注意,国密支持不是"勾一个选项"就结束的。你需要验证协商过程是否真的使用了SM2/SM3/SM4,还是在配置了国密证书后仍然回落到 RSA 套件。我见过某团队自研的国产 Broker 在 openssl 配置里写了ciphers = SM2-SM4-GCM-SM3,但实际连接时客户端不声明国密套件,服务端竟然也接受普通 TLS 1.3 握手,说明它根本没有强制国密算法。这样的配置在等保评审里会被认定为"未正确使用密码技术"。
另一个细节是日志留存。国产 Broker 如果默认只打印运行时日志,没有操作审计日志(谁在什么时候改了什么配置、谁调用了管理 API),就很难满足"安全审计"要求。如果在选型阶段就明确有等保需求,直接要求厂商提供审计日志方案,不要等测评前再去补。
6. 我的选型建议与最终判断
6.1 什么场景下可以果断替换
不是所有系统都适合换,但以下四类场景,我认为替换的收益大于风险:
- 产品同时包含硬件和软件,需要闭源分发:比如充电桩、边缘网关、车载终端,采用 Apache 2.0 的国产协议栈作为内嵌组件,商业上更干净。
- 已经被 Mosquitto 的 EPL 条款纠缠过的项目:如果法务已经提出风险预警,不要继续在 EPL 边缘试探,果断换授权更明确的方案。
- 需要深度定制协议行为的项目:比如在 MQTT CONNECT 阶段要注入自定义设备指纹,或者要对遗嘱消息做特殊处理,国产代码库更便于本地二次开发。
- 有等保或行业合规要求、且需要国密算法的项目:很多国产协议栈原生支持国密套件,这比自己在 Mosquitto 上折腾 OpenSSL 简单太多。
6.2 什么场景下暂时不要动
如果你的系统已经稳定运行超过三年,而且完全满足业务需求,那么因为"追新"去替换是得不偿失的。特别是以下情况,我更倾向于保守:
- 团队没有专职运维,只是部署了 Mosquitto 跑一些小流量的设备数据,那继续用 Mosquitto,只要明确 EPL 边界、并没有修改源码,风险其实很低。
- 团队大量使用了 EMQX 企业版独有的规则引擎和 Dashboard 功能,替换会带来几周的适配工作,而业务又等不起。
- 当前环境完全离线、没有可持续更新的内网源,替换一个社区活跃度低的国产项目,遇到 Bug 会非常被动。
6.3 给团队的一条落地路径
如果决定替换,我建议按"三周法则"来推进:第一周做技术验证,搭建一个与生产环境等价的测试环境,跑完协议兼容性、性能压测、TLS 双向认证、持久化重启测试;第二周做业务接口适配,把所有客户端连接参数、订阅主题、遗嘱消息、ACL 权限逐条映射到新系统,同时补齐日志和监控;第三周做灰度切换,选一条低频业务线先跑 48 小时,观察内存、CPU、消息延迟和错误日志,确认无异常后再全量切换。
我个人的经验是:替换最大的风险从来不是协议栈本身,而是团队对协议栈"边界的理解"——许可证边界、功能边界、运维边界。把这三点想清楚,Mosquitto 和 EMQX 并不是不可替代的。
最后再分享一个小技巧:无论最终选哪款,都要在代码仓库里留一个mqtt-compatibility-test目录,把从 Mosquitto/EMQX 上跑过的回归脚本固化下来。以后无论换国产协议栈还是将来再升级,这套脚本都能帮你在第一天就发现行为差异,避免上线后半夜起来救火。