MQTT实战 - MQTT Broker选型,EMQX vs Mosquitto,我用过的真实对比(八)
今天我们通过下面五大层次递进的方式详细讲解MQTT Broker选型,从"为什么选错"到"怎么选对",层层推进。

01、引入层|三个案例,看清选错的代价
做物联网这八年,我经手的 MQTT Broker 换过三轮。
最早图省事,一台树莓派上跑 Mosquitto,几百个传感器连进来,稳得像块石头,一年到头不用管。后来设备从几百涨到几万,还是这台 Mosquitto,情况就变了——连接数忽多忽少,消息延迟从几十毫秒抖到好几秒,半夜被运维电话叫起来翻日志。再后来上了 EMQX 集群,连接数这件事才算被按住。
案例一:边缘工控机盲目上 EMQX,资源错配
几年前一个边缘侧设备接入项目,客户现场只有一台 4 核 8G 的工控机,要接大约 5000 个传感器,上报频率不高。我当时想:“5000 个连接,EMQX 肯定扛得住,功能还全。”于是上了 EMQX。上线后第一个月:EMQX 在这台工控机上吃掉了 400 多 MB 常驻内存,和工控机上其他采集程序抢资源,GC 抖动导致 P99 延迟偶尔飙到几百毫秒。而这个项目根本用不到集群、用不到规则引擎、用不到 Dashboard——我用一台“航母”去干了一艘“巡逻艇”的活,结果航母的吃水深度把码头压垮了。 后来换 Mosquitto,内存降到 20 MB 以内,同样 5000 连接,P50 延迟反而更低。
案例二:云上 Mosquitto 扛 8 万设备,后期被迫整体迁移
前年一个智慧园区项目,一期用 Mosquitto 跑了不到两万设备,二期要扩到三十万,还要上规则引擎做边缘联动、上共享订阅做消费端水平扩容。团队第一反应是“MQTT 协议都一样,换 Broker 不就是改个 IP 吗”。结果这一换,换了整整六周:Mosquitto 里没有规则引擎,原本在 Broker 里配的数据落库、转发 Kafka、字段过滤全得在业务侧重写;Mosquitto 的集群要么没有,要么靠桥接拼,桥接又不支持通配符订阅,设备状态同步链路直接断了;最坑的是认证,一期图省事全写死在密码文件里,迁移时才发现 Mosquitto 的 password_file 不支持热加载,改一个设备密钥就要重启整个 Broker,三十万设备根本没法运维。最后花了两周把 Mosquitto 上的隐式依赖一条条盘出来,迁到 EMQX 才收场。
案例三:3000 设备的边缘网关,被硬塞了 EMQX 集群
另一个新能源边缘网关项目,设备量不大,但一开始就上了 EMQX 集群,功能确实全、扛得住,可团队被 Erlang 的黑盒调优、内存水位、集群脑裂处置折腾得够呛——一个只有 3000 设备的边缘网关场景,硬是养了一套百万连接级的基础设施,运维成本严重错配。
三个案例指向同一个结论:EMQX 和 Mosquitto 的差距不在“功能列表”上,而在“架构基因”上。Broker 选型不是“选最强的”,而是“选和你设备规模、团队能力、集成需求、运维预算最匹配的”。选错的代价,要么是资源浪费,要么是重构,要么是背着不需要的复杂度过日子。
下面按“认知 → 本质 → 实战 → 决策”的递进顺序展开。三个案例会在后面每个部分逐一对应上。
02、认知层|先立四个认知,否则一定选错
在对比任何参数之前,先把这四条认知立住,后面的结论才能落地。
认知 1:架构决定天花板,不是配置决定天花板
Mosquitto 用 C 语言写的单进程事件循环,EMQX 用 Erlang/OTP 的分布式 Actor 模型。这不是“谁代码写得好”的问题,而是并发模型与可扩展性范式的根本不同:
-
C 语言单进程: 垂直扩展为主,单核事件循环决定单节点连接与转发上限,想加容量主要靠“换更大的机器”;横向扩展要自己搭桥接/外部负载均衡,没有开箱即用的分布式路由与会话同步。
-
Erlang/OTP: 天生分布式,节点组网后 连接、订阅路由、Retain、共享订阅在集群内自动分布,横向加节点即近线性扩容,官方做过单集群 ** 1 亿连接** 的公开压测。
结论:当一个架构“没有原生集群能力”时,你在应用层补多少东西都补不出真分布式。 这是 Mosquitto 与 EMQX 最本质的分水岭,比任何一条配置参数都重要。
认知 2:连接数 ≠ 吞吐量 ≠ 消息速率,别拿一个指标骗自己
很多人压测只盯“能连多少设备”,但真正拖垮 Broker 的往往是消息速率(msg/s)+ 订阅扇出(一条消息要投给多少订阅者)+ QoS 开销。10 万设备低频心跳(每 5 分钟一条)和 1 万设备高频上报(每条扇出给 5 个订阅端),后者的转发压力可能高一个数量级。选型时三个量必须分开估:峰值连接数、峰值 msg/s、平均订阅扇出。
认知 3:功能丰富度是“负债”,不按需买单
EMQX 的规则引擎、SQL、多协议接入(CoAP / LwM2M / STOMP / WebSocket)、Dashboard 很强,但每一项都意味着更多的配置面、更多的 Erlang 调优项、更大的内存基线、更陡的故障排查曲线。Mosquitto 的“什么都没有”在边缘设备上反而是优点——攻击面小、内存占用个位数 MB、行为可预测。为不需要的功能付运维复杂度,是最隐蔽的成本。
认知 4:迁移成本永远被低估——Broker 不是“换个 IP”
案例二的六周,最贵的不是 EMQX 部署,而是盘 Mosquitto 上的隐式依赖:哪些逻辑靠桥接、哪些靠 password_file、哪些靠 $SYS 监控、哪些 Topic 用了 Mosquitto 特有的 ACL 写法。MQTT 协议本身可移植,但你在某个 Broker 上配出来的“用法”不可移植。 选型第一天就要问自己:这东西以后我大概率不会换,现在的能力够不够用 3 年。
还有一个经常被忽略的认知:许可证不是小事。 EMQX 5.8 及以前是 Apache 2.0,5.9 起切换为 BSL(商业源码许可),对“用 EMQX 内核做商业 SaaS 售卖 / 二次分发”有约束;Mosquitto 是 EPL 2.0 / EDL,对商业集成通常更宽松。如果你的产品要内嵌 Broker 随设备出货或对外提供托管,必须先过法务这一关——这条经常到上线前才暴雷。
03、本质层|架构基因、单机性能、集群与高可用、六维硬对比
本层从架构基因出发,逐层拆解两个 Broker 的本质差异:出身背景 → 单机性能 → 集群高可用 → 六维功能对比。
一、认清两个对手:它们各自为谁而生
要理解这两个 broker 为什么长成这样,得先看它们从哪来。
1.1、Mosquitto:为了轻量而生
-
2009 年由 Roger Light 开发,后来捐给 Eclipse 基金会,采用 EPL/EDL 许可。
-
C 语言写的,编译出来是个几百 KB 的可执行文件,GitHub 上约 11k star。
-
核心是单线程事件循环,用 poll/select 做多路复用,没有分布式、没有集群。
-
定位很窄:把 MQTT 这一个协议做好,别的都不碰。
内部事件循环逻辑大致是这样:
while (running) {
poll_fds(fds, nfds, timeout); // 等待网络 I/O 事件
for (i = 0; i < nfds; i++) {
if (fds[i].revents & POLLIN) {
handle_client_message(fds[i].fd); // 顺序处理每个连接
}
}
process_scheduled_tasks(); // 定时任务
}
好处是简单、稳定、几乎不占资源。我见过一台 1 核 1G 的机器跑 800 个设备,CPU 常年低于 5%,内存稳在 50MB 左右,连着跑了两年多,没重启过。坏处也很直白:所有事情排队做,一旦某个客户端消费慢,或者一条大消息把事件循环卡住,后面所有连接都得等。
1.2、EMQX:为了规模而生
-
2012 年在 GitHub 开源,Erlang/OTP 写的。许可分界:5.8 及之前是 Apache 2.0,5.9 起改为 BSL 1.1。
-
GitHub 上约 16.4k star,是目前 star 数最高、提交最活跃的 MQTT broker 项目。
-
从第一版就是无主(masterless)分布式架构,天然为集群设计。
-
官方基准里,单节点跑到过 500 万连接,23 节点集群做过 1 亿连接的公开验证。
Erlang 的进程是轻量级的,一台机器上开几十万、上百万个进程是常规操作。EMQX 里每个连接对应自己的进程,某个连接出问题不会拖垮其他连接——这是它能扛住几十万连接的底层原因。EMQX 5.0 之后把集群的数据底座从 Mnesia 换成了 Mria + RLOG 复制日志:核心节点(Core)负责写和全量复制,副本节点(Replicant)只读、专门处理客户端会话,两者的数据通过复制日志异步同步。这套改动把单集群的连接上限拉到了亿级。
1.3、一张总表看清出身差距
一张表看完“架构基因 → 性能量级 → 集群能力 → 生态与许可”,最后一句画像直接定调。

读这张表的正确方式:不要横向比"谁数值大谁好",而是看你站在哪一列的需求上。左边一栏全部命中你(设备少、资源紧、单机够用、不需要复杂集成),Mosquitto 是最优解;右边一栏开始有硬需求(要集群、要规则引擎、要百万连接、要多协议),EMQX 的价值才兑现。
这两个出身决定了后面所有的差异。C 语言加单线程,是为了把资源占用压到极限;Erlang 加 OTP,是为了把并发上限拉到极限。谈不上谁更先进,只是解决了不同的问题。
二、单机性能:Mosquitto 的资源效率是碾压级的,但"吞吐天花板"不在同一个维度
2.1、实测数据:45000 msg/s 时,18 MiB vs 434 MiB
一个可复现的公开基准测试(同一台 Hetzner CCX23,4 vCPU,Mosquitto 2.0.20 vs EMQX 5.8.6,相同 in-memory 配置,使用 EMQX 自家的 emqtt-bench 驱动)给出了非常直白的数据:
同样的 45,000 msg/s 拐点吞吐下,两者的延迟、CPU 余量与内存占用差距。

怎么读这张表: 两者在 45,000 msg/s 同时到达拐点,但 Mosquitto 仍有 70% CPU 空闲、P99 ≤100ms、内存仅 18 MiB;EMQX 的 CPU 已基本吃满(空闲 2%),P99 拉长到 500ms、常驻内存 434 MiB。拐点之后继续加压,两者都会出现延迟陡增与丢弃,但 EMQX 的余量更先耗尽。
另一项针对物联网数据采集的学术研究显示,Mosquitto 的平均延迟可以低至 1.04 ms,EMQX 在同一测试中为 3.06 ms,差距约 66%。
但这组数据必须放在正确的语境里读:EMQX 的 434 MiB 里,装着规则引擎、数据集成、Dashboard、集群元数据、Erlang VM——这些东西 Mosquitto 根本没有。 如果你把 Mosquitto 配上外部组件去实现同等功能(Prometheus exporter、外部桥接、独立的数据转发服务),资源差距会显著缩小,但架构复杂度会反超。
2.2、消息吞吐量(1KB 消息体,8 核 16G 实测)
测试环境统一为 8 核 16G 云主机,Ubuntu 20.04,同一批 1KB 消息体,QoS 等级逐步提升,QoS 越高,握手与确认开销越大;同一等级下,两者吞吐量级与衰减曲线的差异:

怎么读这张表: QoS 0 → 2,两者吞吐都随握手次数增加而下降;EMQX 在 Q0 / Q1 上约为 Mosquitto 的 1.3–2 倍,到 QoS 2 时降到约 2 万条/秒,但仍明确给出量级且强调低丢失率;Mosquitto 在 QoS 2 下的吞吐受落盘与配置影响较大,表中只给“明显下降”的定性结论。
2.3、延迟与资源占用
空载时两者的 P99 都在 10ms 以内,看不出差别。差距出现在高负载下:万级连接时,Mosquitto 的单线程排队效应显现,P99 能抖到百毫秒甚至秒级;EMQX 因为有并行处理,P99 还能稳在几十毫秒。资源这边则是另一番景象——同样是 2 万连接,Mosquitto 的 CPU 占用约 10%、内存在百 MB 级;EMQX 的 CPU 约 55%,内存也更高。
一个实用的经验判断:
-
单机 5 万连接以内、消息路径简单、延迟敏感 → Mosquitto 的资源效率和延迟表现通常更优。
-
单机超过 5 万连接,或者预期 6–12 个月内会突破 10 万 → 不要硬扛 Mosquitto,直接上 EMQX 集群,或者至少把桥接/HA 方案想清楚。
说白了,Mosquitto 用更少的资源做更小的事,EMQX 用更多的资源换来了并行能力和更高的上限。这两句话就是性能部分的全部总结。
三、集群与高可用:这是两者"基因级"的分水岭
性能差是"快慢问题",集群是"能不能"的问题——这里没有中间地带。
3.1、Mosquitto 没有原生集群,桥接是"路由"不是"集群"
Mosquitto 官方和多个来源都明确:Mosquitto does not have a built-in cluster mode。它能做的多机方案是 Bridge(桥接):一个 Mosquitto 作为另一个 Mosquitto 的 MQTT 客户端,把指定 Topic 的消息按方向转发过去。
桥接能解决消息路由问题(边缘到中心、多站点汇聚、安全边界隔离),但它不是集群,给不了你这些:
-
同步复制: 发到 A 的消息,要等桥接转发才能在 B 可见,中间有延迟和重试窗口;A 在转发前崩溃,这条消息就丢了。
-
统一客户端会话: 客户端从 A 重连到 B,对 B 来说是一个全新会话,A 上的 QoS 1/2 在途消息不会迁移过去。
-
连接负载均衡: 桥接只管消息转发,不管连接分布;客户端侧的高可用需要额外在前面挂 HAProxy 之类的 TCP 负载均衡器。
-
规模恶化: 有生产案例报告,8 个 Mosquitto 桥接在一起,20k 客户端、1k msg/s 的场景下,桥接转发延迟有时超过 2 分钟。学术文献也指出,桥接模式的通信开销会随着集群规模上升而显著增加。
我踩过:两个厂区各一台 Mosquitto,bridge 配错 topic 方向,消息无限回弹,Broker 内存 10 分钟打满。bridge 只适合边缘→云接力,永远不要用 bridge 硬凑高可用。
3.2、EMQX 的 Mria 架构是为"水平扩展"设计的
EMQX 5.0 引入的 Mria 集群架构,核心是 Core + Replicant 节点角色分离:Core 节点管理认证、会话状态和集群控制,Replicant 节点只中继消息、不存储会话数据,从而降低内存开销、提升可扩展性。官方给出的数字是单集群 23 节点、1 亿并发 MQTT 连接。第三方压测中,EMQX 5.10 集群也展示了 200 万 WebSocket 并发连接、10 万 TPS 的处理能力。
但这不意味着 EMQX 集群是"免费"的。 集群本身就带来新的成本:
-
网络延迟: 跨节点、跨机房的同步和路由有网络开销,EMQX 跨域集群时实例间网络连接速度慢是一个明确的挑战。
-
运维复杂度: Erlang 集群的节点发现、网络分区处理、License 行为变化(5.9.0+)都是实战中的坑点。
-
奇数节点原则: EMQX 社区建议 Core 节点用 3/5/7 的奇数配置,但不要过多,因为集群规模增大后同步开销会上升。
3.3、集群端口:第一次搭最容易漏的坑
对外暴露的只有 MQTT / WebSocket / Dashboard 三类;Erlang 内部端口不要对外放行。
第一次搭 EMQX 三节点集群,docker-compose 起是起来了,Dashboard 里死活只看到一个节点。折腾半天,发现是集群通信端口没在安全组里放通。记住这几个端口:

安全提醒: 生产环境对外只需放行 8883(或 1883 内网)、8084、18083(建议加白名单);4370、5369/5370 属 Erlang 集群内部通信,严禁暴露到公网,否则可被直接接管集群。
四、功能特性逐项对比:EMQX 是"平台",Mosquitto 是"管道"
下面这张表是我按实际使用频率整理的,不是照抄官网特性列表。

4.1、规则引擎与数据集成:这是 EMQX 最明显的差异化能力
EMQX 杀手锏——一条 SQL 就把设备消息过滤、富化、转 Kafka / 落库 / 调 HTTP:
SELECT
payload.device_id AS device_id,
payload.temp AS temperature,
ts
FROM
"svc/+/telemetry"
WHERE
payload.temp > 80
-- 动作:INSERT INTO kafka-telemetry / save into postgres / POST webhook-alert
Mosquitto 没有规则引擎,等价能力要在外部写订阅者程序 + 自己处理重试/背压。小规模你本来也要写消费者,无所谓;一旦"多源转发 + 过滤 + 富化"需求多起来,自研会迅速变成维护噩梦。
这意味着什么? 如果你的架构是"Broker 只负责收发,后面用 Kafka/消费者做数据处理",Mosquitto 的"没有规则引擎"不是缺点,反而符合关注点分离。但如果你的团队没有独立的数据管道,希望 Broker 顺手把数据落到时序库/消息队列,EMQX 的规则引擎能省掉一个常驻服务——少了一个服务,就少了一个故障点。
4.2、认证与 ACL 动态化
Mosquitto 的 ACL 是文件级的、静态的,改完要重载或重启。2.0 引入了动态安全插件,能在线管理用户、分组和 ACL,但能力仍然有限,要接企业 OAuth/JWT/一机一密得自己写插件。
EMQX 提供内置的认证后端(数据库、JWT、X.509 等)、命名空间隔离、细粒度授权,ACL 占位符做 Topic 隔离非常顺手:
# EMQX 典型 Topic 级隔离授权(每个客户端只能读写自己的前缀)
svc/device/${clientid}/telemetry # publish allow
svc/device/${username}/# # subscribe allow(按用户名隔离)
对于 500 台设备、5 个运维人员的团队,Mosquitto 的"配置文件 + ACL 文件 + 外部 Prometheus exporter"完全够用。对于 50 万设备、需要按租户隔离权限、需要开箱即用监控面板的场景,EMQX 的内置能力节省的是"搭一套配套系统"的工程时间。
4.3、共享订阅:分布式消费的刚需
MQTT 5 的 $share/group/topic 共享订阅,是 .NET 后端多实例部署、避免重复消费的核心能力。
-
Mosquitto 支持共享订阅,但实现较基础,不适合后端多实例大规模负载均衡消费;
-
EMQX 原生完善共享订阅策略(random / round_robin / sticky / hash),消息负载均衡和消费故障转移开箱即用。
这是做分布式物联网平台的刚需,没有这个能力,你的后端服务多实例部署时要么重复消费、要么自己在 Redis 里造消费锁。
4.4、协议支持
Mosquitto 支持 MQTT 5.0、3.1.1、3.1,以及 TLS 和 WebSocket。EMQX 额外支持 MQTT-SN、MQTT over QUIC,以及更丰富的桥接目标。如果你已经在用 MQTT over QUIC 做弱网/移动场景,EMQX 是开箱即用的;Mosquitto 不提供这个能力。
04、实战层|部署配置、运维可观测、安全加固、踩坑实录
本层聚焦生产落地:部署配置代码可直接抄、运维可观测性对比、安全加固四步法、以及真实踩坑实录。
五、部署与配置实战:两套都给你能直接抄的版本
5.1、Mosquitto:生产基线(单机 / 边缘)
Docker 起一个最省事:
docker run -d --name mosquitto \
-p 1883:1883 -p 8883:8883 \
-v $PWD/mosquitto.conf:/mosquitto/config/mosquitto.conf \
-v $PWD/data:/mosquitto/data \
-v $PWD/certs:/etc/mosquitto/certs \
-v $PWD/passwd:/etc/mosquitto/passwd \
-v $PWD/acl:/etc/mosquitto/acl \
eclipse-mosquitto:latest
配置文件 mosquitto.conf 里,生产环境至少要盯住这几项(生产加固版):
# ---------- 监听与 TLS ----------
listener 1883
allow_anonymous false # 生产必须关匿名(2.0 起默认就是 false,别改回去)
listener 8883
certfile /etc/mosquitto/certs/server.crt
keyfile /etc/mosquitto/certs/server.key
cafile /etc/mosquitto/certs/ca.crt # 双向认证再加 require_certificate / tls_version
# ---------- 认证(注意:password_file 不支持热加载,改密码需重启) ----------
password_file /etc/mosquitto/passwd
acl_file /etc/mosquitto/acl
# ---------- 持久化 / 会话 ----------
persistence true
persistence_location /mosquitto/data
autosave_interval 60 # 定期落盘,重启保留 retained/会话
# ---------- 心跳与连接 ----------
max_keepalive 120 # 客户端 keepalive 上限,防长连接假在线
connection_messages true # 连/断写日志,便于排查
# ---------- 系统主题($SYS,便于接监控) ----------
sys_interval 10
# ---------- 资源:单机连接数受 FD 限制,配合系统 ulimit ----------
max_queued_messages 1000 # 单客户端排队上限,防内存打爆
max_inflight_messages 20
max_connections 50000
message_size_limit 65536
ACL 配置示例(Topic 级隔离):
# /etc/mosquitto/acl —— Topic 级隔离示例
user device_agent
topic readwrite svc/device/gw_parkA_001/#
user monitor
topic read $SYS/#
topic read svc/#
健康检查命令($SYS 计数器 + 端口探活):
# 单主题(monitor 用户,8883 必须带 cafile)
mosquitto_sub -h localhost -p 8883 --cafile /etc/mosquitto/certs/ca.crt \
-u monitor -P '你的密码' -t '$SYS/broker/uptime' -C 1 -W 3
# 双主题
mosquitto_sub -h localhost -u monitor -P '你的密码' \
-t '$SYS/broker/messages/dropped' -t '$SYS/broker/clients/connected' -C 2 -W 3
流传很广的坑:网上大量老教程里还写着 allow_anonymous true,Mosquitto 2.0 之前确实默认允许匿名,但那之后已经默认禁止了。照抄老配置把匿名打开,等于把大门敞开——我早期一个项目就是这么被扫公网的人塞满了垃圾主题。
5.2、EMQX:docker-compose 三节点集群 + 认证 + 规则转发 + Prometheus
单机Docker启动:
docker run -d --name emqx \
-p 1883:1883 -p 8083:8083 -p 8883:8883 -p 18083:18083 \
-e EMQX_NODE_NAME=emqx@127.0.0.1 \
emqx/emqx:5.8
5.8 为 Apache 2.0 线;5.9+ 为 BSL,注意许可
三节点集群用 docker-compose(生产参考骨架):
# docker-compose.yml —— EMQX 5.x 三节点集群
services:
emqx1:
image: emqx/emqx:5.8
hostname: emqx1
environment:
EMQX_NODE__NAME: emqx1@emqx1.emqx.io
EMQX_NODE__COOKIE: change_me_cluster_cookie
EMQX_LOG__CONSOLE__LEVEL: warning
EMQX_MQTT__MAX_SESSION_EXPIRY_INTERVAL: 300s
EMQX_CLUSTER__DISCOVERY_STRATEGY: static
EMQX_CLUSTER__STATIC__SEEDS: "[emqx1@emqx1.emqx.io,emqx2@emqx2.emqx.io,emqx3@emqx3.emqx.io]"
ports: ["1883:1883", "8883:8883", "18083:18083"]
volumes: ["emqx1_data:/opt/emqx/data"]
extra_hosts:
- "emqx1.emqx.io:172.28.0.11"
- "emqx2.emqx.io:172.28.0.12"
- "emqx3.emqx.io:172.28.0.13"
networks:
emqx_net:
ipv4_address: 172.28.0.11
emqx2:
image: emqx/emqx:5.8
hostname: emqx2
environment:
EMQX_NODE__NAME: emqx2@emqx2.emqx.io
EMQX_NODE__COOKIE: change_me_cluster_cookie
EMQX_CLUSTER__DISCOVERY_STRATEGY: static
EMQX_CLUSTER__STATIC__SEEDS: "[emqx1@emqx1.emqx.io,emqx2@emqx2.emqx.io,emqx3@emqx3.emqx.io]"
volumes: ["emqx2_data:/opt/emqx/data"]
extra_hosts:
- "emqx1.emqx.io:172.28.0.11"
- "emqx2.emqx.io:172.28.0.12"
- "emqx3.emqx.io:172.28.0.13"
networks:
emqx_net:
ipv4_address: 172.28.0.12
emqx3:
image: emqx/emqx:5.8
hostname: emqx3
environment:
EMQX_NODE__NAME: emqx3@emqx3.emqx.io
EMQX_NODE__COOKIE: change_me_cluster_cookie
EMQX_CLUSTER__DISCOVERY_STRATEGY: static
EMQX_CLUSTER__STATIC__SEEDS: "[emqx1@emqx1.emqx.io,emqx2@emqx2.emqx.io,emqx3@emqx3.emqx.io]"
volumes: ["emqx3_data:/opt/emqx/data"]
extra_hosts:
- "emqx1.emqx.io:172.28.0.11"
- "emqx2.emqx.io:172.28.0.12"
- "emqx3.emqx.io:172.28.0.13"
networks:
emqx_net:
ipv4_address: 172.28.0.13
networks:
emqx_net:
driver: bridge
ipam:
config:
- subnet: 172.28.0.0/24
volumes:
emqx1_data: {}
emqx2_data: {}
emqx3_data: {}
启用规则引擎(遥测 > 80℃ 转发 Kafka + 告警 HTTP):
# 1) 登录拿 token
TOKEN=$(curl -s -X POST http://localhost:18083/api/v5/login \
-H "Content-Type: application/json" -d '{"username":"admin","password":"public"}' | jq -r .token)
# 2) 先建 webhook bridge(名称 alert-svc),再建 kafka bridge(名称 kafka-telemetry)
curl -s -X POST http://localhost:18083/api/v5/bridges -H "Authorization: Bearer $TOKEN" \
-H "Content-Type: application/json" \
-d '{"name":"alert-svc","type":"webhook","enable":true,"url":"http://alert-svc:8080/hook"}'
# 3) 建规则(action 用 webhook:kafka: 前缀)
curl -s -X POST http://localhost:18083/api/v5/rules -H "Authorization: Bearer $TOKEN" \
-H "Content-Type: application/json" \
-d '{"sql":"SELECT payload.device_id AS device_id, payload.temp AS temp, ts FROM \"svc/+/telemetry\" WHERE payload.temp > 80","actions":["kafka:kafka-telemetry","webhook:alert-svc"]}'
注意:
-
默认 Dashboard 账号是 admin / public,首次登录会强制改密码;
-
节点名格式:emqx@主机名,集群里各节点必须能互相解析这个名字;
-
生产集群至少 3 节点(奇数,防脑裂),不要单节点承载核心业务。
5.3、客户端侧封装:换 Broker 不动业务代码
我用 MQTTnet 5.x 写一段与 Broker 解耦的连接韧性封装——把认证、TLS、Topic 授权等差异全部挡在配置层,业务代码只认 host/port/cred/topic/QoS。重点演示了 5.x 的 MqttClientFactory、SessionExpiryInterval 的正确用法,以及断线后指数退避重连的实现。将来从 Mosquitto 迁到 EMQX,业务代码一行不动。
// 客户端侧(MQTTnet 5.x):与 Broker 解耦的连接韧性写法,
// 把 Broker 差异隔离在配置层,业务代码只认 host/port/cred/topic/QoS。
using MQTTnet;
var opts = new MqttClientOptionsBuilder()
.WithTcpServer(host, port) // mosquitto / emqx 仅改 host
.WithCredentials(user, pass)
.WithClientId(clientId)
.WithKeepAlivePeriod(TimeSpan.FromSeconds(60))
.WithCleanStart(false)
.WithSessionExpiryInterval(300) // 5.x 收 uint(秒),不是 TimeSpan
.WithTimeout(TimeSpan.FromSeconds(10))
.Build();
var factory = new MqttClientFactory(); // 5.x 用 MqttClientFactory,非 MqttFactory
var client = factory.CreateMqttClient(); // IMqttClient
// 5.x 无 managed client:在断线事件里自己实现指数退避重连
client.DisconnectedAsync += async e =>
{
if (!e.ClientWasConnected) return;
var delay = TimeSpan.FromSeconds(3);
while (true)
{
try { await client.ReconnectAsync(CancellationToken.None); return; }
catch { await Task.Delay(delay); }
}
};
await client.ConnectAsync(opts, CancellationToken.None);
// 接收消息注意:5.x 的 e.ApplicationMessage.Payload 是 ReadOnlySequence<byte>
封装要点:Broker 的差异(认证方式、TLS、Topic 授权、集成方式)应被挡在配置 + 网关层之外。这样将来从 Mosquitto 迁到 EMQX,业务代码一行不动。
5.4、把它们桥在一起:边缘 Mosquitto + 云 EMQX(推荐的生产架构)
设备量大又不全都要上云时,最成熟的玩法是:现场边缘放一台 Mosquitto 收敛数据,只把需要的主题桥接到云上的 EMQX,云端再用规则引擎做处理和落库。 Mosquitto 侧的桥接配置:
# 边缘 mosquitto,端口 1884;
listener 1884
allow_anonymous true
persistence false
# 桥接:边缘 -> 云
connection cloud_emqx
address mqtt-cloud:2883
topic sensor/# out 1 # 本地 sensor/ 下的消息发往云端,QoS 1
topic cmd/# in 1 # 订阅云端下发到本地的指令
bridge_protocol_version mqttv311
cleansession false # 保留会话,断线重连不丢离线消息
restart_timeout 5
remote_username bridge_user
remote_password bridge_pass
# 云 broker(充当 EMQX 的角色),端口 2883,允许匿名(贴近 EMQX 默认)
listener 2883
allow_anonymous true
persistence false
桥接最容易漏配的三处:topic 的方向(out / in / both)、QoS 等级、cleansession 与重连策略。方向写反了消息就单向流,clean session 设成 true 则每次重连都会丢弃离线消息——这三个参数一定要配全再上生产。
六、运维与可观测性:一个"简单但手工",一个"强大但需要理解"
Mosquitto 的运维基本靠命令行加日志,再配合 $SYS 系统主题自己拼监控。想看当前连接列表、想临时踢掉某个客户端、想看实时消息速率,都得自己写脚本或者翻日志。小规模时这不算问题,反正设备就那么多,出了问题肉眼也能看出来。
EMQX 自带 Dashboard,连接、订阅、主题树、消息速率、告警都能在页面上看;REST API 方便接进自家的运维平台;Prometheus 和 OpenTelemetry 也打通了,直接挂 Grafana + AlertManager 就行。设备一旦上万,多团队协作,EMQX 这套可观测性省下来的排查时间,折算成人力成本是很实在的。
一句话:Mosquitto 的运维成本集中在"你要自己搭",EMQX 的运维成本集中在"你要理解它"。 前者适合"不想引入复杂系统"的团队,后者适合"愿意为规模能力付出学习成本"的团队。
EMQX 生产运维的六个关键监控指标,六个指标覆盖“连接异常 → 容量预警 → 丢弃与失败”三类风险,阈值按 P0–P3 分级。

使用建议: messages.dropped 与规则引擎动作失败率属于 “只要不为 0 就立刻查” 的 P0 指标;连接数与吞吐是容量趋势类 P2,适合做扩缩容与容量规划;Erlang 进程 / 内存与认证失败率是 P1,分别对应 OOM 前兆与安全事件。
七、安全:别在这上面栽跟头
我见过不止一个项目栽在 broker 的默认配置上。一个朋友的公司,Mosquitto 直接暴露在公网,用的还是默认配置,被扫到之后,第二天 broker 里全是垃圾主题,设备消息全被淹没。
安全加固按成本从低到高排,基本是这样几步:
-
关闭匿名访问,启用用户名密码认证——这一步几乎零成本,必须做。
-
启用 TLS,把 1883 明文换成 8883 加密;内部网络也建议做(内网不等于安全,横向移动是攻击者最常用的路径)。
-
配置 ACL,做主题级权限,避免一个设备能订到别人的数据;EMQX 用 ${clientid} 占位符做一机一权最方便。
-
对安全要求高时上双向认证(mTLS),给每个设备发客户端证书。
-
限制单客户端连接数、消息速率、消息体大小,防止恶意连接或失控设备打爆 Broker。
EMQX 在这块额外多几种认证方式,JWT、LDAP、HTTP 回调、Flapping 检测都支持。设备量大、需要集中统一认证时,这些能省掉自己写认证服务的功夫。
八、真实踩坑实录:我交过的学费
我把 Mosquitto 和 EMQX 在生产里各自最容易翻车的点摊开来写——从单机 CPU 打满、内存错配、bridge 回弹、BSL 许可暴雷,到集群端口漏配、Retain 无 TTL、License 隐性上限,十个真实事故对应十个修复方案。看完这一节,你至少能避开我交过的所有学费。
10 个选型翻车现场:场景 → 选了 → 翻车点 → 修复,每一条都是真金白银换来的。

一句话教训:Mosquitto 的坑是"规模/HA 的天花板",EMQX 的坑是"复杂度与许可"。 选错方向,坑一定在后面等你。
05、决策层|场景表、决策树、迁移路径、成本分析、自检清单与口诀
本层给出可执行的决策工具:场景选型表、命中即停决策树、迁移路径、成本分析、自检清单和选型口诀。
九、按场景对号入座 + 选型决策树
9.1、一张场景选型表
对号入座,30 秒定位该选谁——从本机调试、边缘网关、资源受限设备,到上万连接、集群高可用、规则引擎、混合架构,12 个典型场景直接给出推荐方案和一句话理由,不用再翻前面的章节。

9.2、决策树:命中即停
不用通读全文也能拍板——四步递进式判断,每一步命中即停:从连接量与吞吐预估,到集群/HA/规则引擎需求,再到消息后处理能力,最后评估存量迁移成本。跟着树走到底,结论自然浮现;文末一句口诀兜底,五项指标里只要踩中"高并发/要HA/要规则引擎"任意一条,直接锁 EMQX,其余场景 Mosquitto 更省心。
规则:从 Q1 开始,按你的实际情况选择分支;命中结果即停止。多维度冲突时,以连接量与集群需求为第一优先级。

⚠ 常见误区: 只看连接数选 Broker,忽略“规则引擎、共享订阅、桥接、认证体系”等隐式能力;迁移时才发现接入层要重写,成本远超预期。
一句话总结: 小规模/边缘 → Mosquitto / NanoMQ;中等单机 → Mosquitto 精调 或 EMQX 单节点;大规模/高可用 → EMQX / VerneMQ(强合规选 HiveMQ);要省心 → 云托管。迁移先盘隐式依赖,再动手。
9.3、别信这 3 句选型毒鸡汤
❌ “MQTT Broker 都差不多,换个名字而已”
✅ 单机 broker 和分布式接入层,是两种架构。
❌ “先 Mosquitto,以后再迁移”
✅ 设备几万台再迁,客户端不动,但鉴权模型 / topic 规范 / 数据流转全要重做。
❌ “EMQX 太重,小项目不配用”
✅ 如果你项目第一天就有:多业务系统消费、多租户、JWT、Kafka、SLA 监控——小连接数也可以先用 EMQX,别等规模逼你重构。
9.4、还有一类你没问但绕不开的选项
选型不是只有 EMQX 和 Mosquitto 两个点,六种选型,从边缘单机到云托管:先看定位,再看“什么时候认真考虑它”:

决策真相: 往往不是"EMQX vs Mosquitto",而是"要不要自建集群"——如果不想养 Erlang 集群运维,EMQX Cloud / 云托管 MQTT 才是和 Mosquitto 同台竞争的选项。
十、从 Mosquitto 平滑迁移到 EMQX:五步零缺口
如果你已经在 Mosquitto 上,考虑往 EMQX 迁,这份“隐式依赖清单”逐条盘,能帮你提前估出真实工作量,八项迁移逐一对照:左边是 Mosquitto 的旧写法,右边是迁到 EMQX 必须做的改造。

迁移顺序建议: 先改认证与 ACL(语法不兼容,最容易卡住上线),再用规则引擎替掉外部脚本,最后切原生集群并回归 Retain 路由语义;端口与 TLS 放在联调阶段统一重配。
迁移的真实成本 = 协议层几乎为零 + 用法层(认证/ACL/桥接/落库/监控)几乎全要重做。 把“换 Broker”当成“换中间件 + 重做接入层”来估工期,才不会翻车。
平滑迁移路径:
-
先解耦代码: 把应用对 Broker 的依赖收口到配置层(见 1.3 封装),确保“换 host 就能换 Broker”。
-
统一认证: 把 Mosquitto 的 password_file/acl 导成 EMQX 的内置/数据库认证与 ACL,逐客户端比对。
-
对齐 Topic 与 QoS: 保持 Topic 命名、端到端 QoS=min(发布,订阅)、Retain 语义不变,避免迁移叠加语义变更。
-
双跑 + 影子流量: 用 EMQX bridge/双发,把一部分客户端灰度到新集群,比对端到端 seq diff(《丢包排查》系列的对账法)零缺口后再切全量。
-
会话与遗嘱: 确认 SessionExpiry / WillDelay / Retain 行为一致(《遗嘱消息+心跳》系列),尤其 Retain 历史快照的消费侧去重逻辑照搬。
反向(EMQX → Mosquitto)通常只发生在规模缩小 / 下沉边缘场景,代价相对小,但意味着你主动放弃集群、规则引擎、共享订阅。
十一、成本与团队:选型的隐形成本
Mosquitto 几乎没有学习成本,一个人就能维护。适合小团队,或者那种"先把事做出来"的阶段。EMQX 功能全,但集群、规则引擎、数据桥接的配置项多,团队的 Erlang 门槛客观存在,更适合有专门运维或平台团队的中大型项目。
选型时容易只算服务器的钱,忽略了人的时间。一台 Mosquitto 省下的可能是一台机器的开销,但一套 EMQX 省下的可能是运维天天半夜爬起来排查的精力。这两笔账要一起算。
服务器成本对比(同负载下的经验值),同一条规模曲线上的两套方案:小规模 Mosquitto 更省,过万后 EMQX 的架构优势开始兑现。

十二、上线前选型自检清单
选型拍板前最后一道关卡——14 个检查项覆盖连接/吞吐估算、HA与规则引擎需求、协议扩展、许可合规、认证授权、可观测性、团队运维能力、真实压测与迁移规划。先估算规模,再逐条过“是否必须”,最后确认团队与迁移成本,全部通过再上线,避免"上线即翻车"。

十三、选型口诀
Mosquitto 和 EMQX 不是强弱之争,是“单机轻量”与“分布式云原生”两种出身之争——前者赢在小、省、单点够用,后者赢在集群、生态、规模天花板。
看规模、看 HA、看集成、看协议、看资源,再看许可。 五个"看"里,只要高并发、要真高可用、要规则引擎任意一条成立,就上 EMQX——它的天花板是 Mosquitto 用 bridge 永远补不出来的;反过来,资源紧、规模小、集成浅、团队轻运维,Mosquitto 的"小而美、个位数 MB、行为可预测"就是别人拿百万连接架构换不来的优势。
没有最好的 broker,只有最合适的。Mosquitto 用约 20MB 的启动内存,证明了"简单"的价值;EMQX 用亿级连接,证明了"规模"的价值。真正要避免的,是拿一台 Mosquitto 硬扛十万设备,或者为了 800 个传感器上一整套 EMQX 集群——前者是给自己挖坑,后者是给别人交学费。
成熟物联网架构,大多不是二选一:边缘侧用 Mosquitto 做本地收敛,中心侧用 EMQX 集群做平台大脑,中间用 bridge 打通。这个模式把 Mosquitto 的"轻"和 EMQX 的"大"分别放在了正确的位置上,也避免了你在一台 4 核工控机上硬塞 EMQX、或者在十万级设备云上硬扛 Mosquitto 的尴尬。
06、最后
我整理了一份《MQTT Broker选型对比表》,包含:
-
四大底层认知公理(架构决定天花板、连接数≠吞吐≠扇出、功能丰富即负债、迁移成本与 BSL 许可风险)
-
单机性能与集群本质对比(同吞吐拐点下 18MiB vs 434MiB 实测数据、Bridge 非集群与 Mria 原生集群差异、六维硬对比速查)
-
生产级配置基线(Mosquitto 单机加固配置、EMQX 三节点 docker-compose 集群模板、MQTTnet 客户端解耦封装)
-
十个真实踩坑复盘与安全加固四步法(Bridge 方向配反、集群端口未放通、Retain 未设 TTL 等,及关闭匿名/TLS/ACL 规范)
-
场景选型表与命中即停决策树(按规模、HA、集成需求对号入座)
-
平滑迁移五步法与隐式依赖清单(代码解耦、统一认证、对齐语义、双跑灰度、切流验证)
-
14 项上线前选型自检清单 + 一句话选型口诀
私信「MQTT Broker选型对比表」领取。
做物联网 MQTT 的朋友可以一起交流。后面我会在这个号持续更新这个系列,也会穿插一些软考架构师备考、技术管理转型、嵌入式学习的记录。

浙公网安备 33010602011771号