MQTT、EMQX、RabbitMQ、RocketMQ、Kafka:五大消息中间件对比与选型指南
1. 先说结论:这五个名字不是同一层的东西
我在不少技术群里见过同一个问题:MQTT、EMQX、RabbitMQ、RocketMQ 和 Kafka 到底有什么区别?为什么有的叫"协议",有的叫"消息中间件",有的叫"服务器"?有人用 MQTT 做智能家居,有人用 Kafka 做日志采集,还有人用 RabbitMQ 做订单异步处理,看起来都叫"消息",但选型思路完全不同。
要搞懂这几个东西的关系,先记住一句话:MQTT 是协议,EMQX 是产品,RabbitMQ、RocketMQ、Kafka 是各自独立的消息中间件实现。把"协议"和"中间件"放在一起对比,本身就是很多初学者困惑的根源。
用快递做类比:假设你要寄一个包裹,MQTT 协议就像是"寄件规则手册",规定了包裹怎么打包、怎么填单、快递员怎么跟你确认签收;而 RabbitMQ、RocketMQ、Kafka、EMQX 这些中间件,是真正在运转的"快递公司"——它们有自己的仓库(存储)、分拣线(路由)、配送车辆(网络传输),并且各自遵循不同的运营规则。
这篇文章适合三类人:一是正在做技术选型、需要在多个消息中间件之间做决策的开发者;二是刚接触消息队列、被各种名词绕晕的初学者;三是做物联网、车联网、工业采集方向,需要把 MQTT 和 EMQX 用起来的工程师。我会把五个名字从底层设计到适用场景全部拆开讲透。
1.1 MQTT 和 EMQX 是什么关系
先解决最容易混淆的一对。
MQTT 全称 Message Queuing Telemetry Transport,是一种发布/订阅模式的消息传输协议,它本身不是可运行的软件。协议规定的是"消息长什么样""客户端怎么连接服务器""断开后怎么重连""消息怎么确认",但不关心服务器用什么语言实现、消息存在哪里。
EMQX 则是基于 MQTT 协议实现的一个消息服务器(Broker),它是可安装、可运行的软件。EMQX 的作用是接收大量设备通过 MQTT 协议发来的连接和消息,然后按主题(Topic)进行路由转发。打个比方:MQTT 是普通话的语法规则,EMQX 是一个讲普通话的话务中心。
常见的 MQTT Broker 不止 EMQX 一家,还有 Mosquitto、HiveMQ、VerneMQ。EMQX 能火起来,主要是因为它在百万级连接、集群扩展、规则引擎这几个方向做得非常扎实,而且开源且有商业版支持。后面我在第 2 节会详细拆。
1.2 RabbitMQ、RocketMQ、Kafka 又是怎么回事
RabbitMQ 最早基于 AMQP 协议实现,后来也支持了 MQTT、STOMP 等多种协议。它是一个通用的消息代理,特点是功能全面、路由灵活、生态成熟,在企业内部系统解耦、任务分发场景里用得非常多。
RocketMQ 是阿里巴巴开源的消息中间件,核心场景是电商交易、订单处理这类对消息不丢失、严格有序有高要求的业务。它经历过双十一的流量考验,在金融交易类场景里口碑很好。
Kafka 最初由 LinkedIn 设计,后来捐给 Apache 基金会。它的核心设计是分布式提交日志,天生适合海量日志采集、数据管道、流式计算。Kafka 的吞吐量极高,但它的定位从一开始就不是"通用的业务消息中间件",而是"处理流数据的骨干系统"。
1.3 消息中间件究竟解决了什么问题
聊具体产品之前,先搞清楚这一类东西存在的意义。消息中间件解决的核心问题有三个:
第一是解耦。系统 A 产生一个事件,如果直接调用系统 B 的接口,B 挂了 A 就失败;如果同时还要通知 C、D、E,A 的代码会越来越复杂。引入中间件后,A 只把消息发给中间件,谁需要谁自己订阅,A 不关心下游有多少个系统。
第二是削峰填谷。电商大促时瞬时订单量可能是平时的几十倍,后端数据库扛不住。消息中间件像一个蓄水池,先把大量请求接著,后端按自己的处理能力慢慢消费,保证系统不被打垮。
第三是异步化。用户下单后,不需要同步等待积分、短信、推荐系统全部处理完,订单服务发一条消息就立刻返回,其余环节异步执行,用户体验和系统吞吐量都能提升。
当然,引入消息中间件也带来新的复杂度——消息可能丢失、可能重复、可能堆积,需要额外保证一致性。这也是为什么不同中间件在可靠性、顺序性、吞吐量之间做出了差异化设计。
2. 逐个拆解:每个中间件的核心设计与适用场景
2.1 MQTT 协议:为物联网而生的轻量级协议
MQTT 诞生于 1999 年,当时 IBM 为了解决石油管道远程通信的问题设计了它。它的核心设计目标用四个字概括:省、小、轻、稳。
省,指的是省流量。MQTT 的固定报文头最小只有 2 个字节,对比 HTTP 动辄几百字节的头部开销,在 2G、3G、NB-IoT 这种弱网环境下优势极其明显。小,指的是客户端实现简单,在 STM32、ESP32 这类资源受限的单片机上都能轻松移植——网上搜"MQTT 协议在 STM32 上的移植",能找到大量实战案例,这是 HTTP 协议做不到的。
轻,指的是协议模型简单。MQTT 只有三种角色:Broker(服务器)、Publisher(发布者)、Subscriber(订阅者),通过主题(Topic)进行消息路由,主题支持层级通配符,例如订阅 sensor/+/temperature 就能收到 sensor/room1/temperature 和 sensor/room2/temperature 的消息。
稳,指的是它专门为不稳定的网络设计了三层保障:QoS 0(最多一次)、QoS 1(至少一次)、QoS 2(恰好一次),以及遗嘱消息(LWT)和持久会话机制。设备突然断网,Broker 可以代发遗嘱消息通知其他设备;设备重连后,可以接着收离线期间的消息。这三点对物联网场景是刚需。
我见过很多人问"MQTT 和 HTTP 哪个好",其实这问题不成立。HTTP 是请求-响应模型,适合浏览器和服务器之间的数据获取;MQTT 是发布-订阅模型,适合设备与设备、设备与平台之间的实时通信。你要做智能家居传感器上报,用 MQTT 是自然选择;你要做一个网页的登录接口,用 MQTT 就属于乱来了。
MQTT 协议的另一个优点是生态工具完善。调试阶段很多人用 MQTTX,这是一个跨平台的 MQTT 客户端工具,可以一键连接 Broker、发布消息、订阅主题,还能模拟多个客户端同时在线。"用 MQTTX 怎么连 EMQX"是我见过最常见的新手问题,连接时只需要填 Broker 地址、端口、用户名密码,默认端口 1883,WebSocket 端口 8083,TLS 端口 8883,就能连上。
2.2 EMQX:MQTT 生态里最能打的 Broker
EMQX 全称 Erlang MQTT Broker,核心是用 Erlang/OTP 语言写的。选 Erlang 不是偶然——这门语言天生擅长高并发、分布式、容错,电信领域用它写了多年的交换机系统。物联网场景的特点是设备数量巨大、连接长驻、消息小而频繁,这跟电信交换机的负载模型非常像。
EMQX 最核心的优势是连接密度极高。单节点支持百万级 MQTT 连接,集群可以扩展到千万级,而且连接数上去之后,消息吞吐和延迟依然能保持稳定。这一点对车联网、共享设备、智慧城市项目极其重要——你可能同时管理几十万块电表,每块电表每 5 秒上报一条数据,对 Broker 的连接管理能力和消息转发能力要求非常高。
除了当 MQTT Broker,EMQX 还内置了强大的规则引擎。它可以直接在 Broker 层对消息做 SQL 形式的过滤、转换,然后转发到 Kafka、数据库、HTTP 服务等。比如设备上报的原始数据是 JSON,规则引擎可以先做字段提取和格式转换,再写入后端存储,省去了自己写一套消费转发服务的成本。
需要注意一个容易踩坑的地方:EMQX 的开源版和企业版功能差异不小。开源版(社区版)支持核心的 MQTT 接入、集群、规则引擎基础功能,但一些高级能力如数据集成(内置连接各类数据库)、Kafka 桥接的部分高级用法、多租户管理等是企业版才有的。做项目前先确认需求范围,不要到部署完了才发现需要的功能不在开源版里。
EMQX 的使用教程在网上一搜一大把,从 Windows 下载安装包到 Linux 上通过 Docker 一键部署都很成熟。新手入门时我建议直接用 Docker 起一个单节点做实验,命令就三行:
启动后浏览器访问 http://localhost:18083 ,默认账号 admin、密码 public,用 MQTTX 连上 1883 端口就能开始测试消息收发。整个过程十分钟都不用,比直接在生产环境踩坑有效率得多。
2.3 RabbitMQ:老牌可靠的消息代理
RabbitMQ 是目前企业级应用中使用最广泛的开源消息中间件之一,2007 年发布,基于 Erlang 编写,核心实现了 AMQP 0-9-1 协议——那是一个定义了消息模型、路由规则、确认机制非常完整的协议。后来 RabbitMQ 也通过插件支持了 MQTT、STOMP 等协议,可以当成多协议网关使用。
RabbitMQ 的设计最大的特色是灵活的路由模型。AMQP 协议里有一个 Exchange(交换机)的概念,消息不是直接发到队列,而是先发给交换机,交换机再根据路由键(Routing Key)和绑定关系把消息投递到对应的队列。交换机有四种类型:Direct(精确匹配)、Topic(通配符匹配)、Fanout(广播)、Headers(头匹配)。这个设计让 RabbitMQ 在处理复杂业务路由时非常得心应手。
举个例子:一个订单系统,订单创建后需要同时通知库存服务扣库存、通知物流服务创建运单、通知用户服务发短信。用 RabbitMQ 的 Topic 交换机,订单服务只需要往 order.created 主题发一条消息,库存服务订阅 order.#,物流服务订阅 order.created,用户服务订阅 order.*,各取所需,互不干扰。
RabbitMQ 的另一个特点是管理界面好用。安装后启用 rabbitmq_management 插件,浏览器打开 15672 端口就能看到队列状态、连接数、消息速率、消费情况,还可以直接在界面上手动发布消息、查看消息内容。对开发和运维来说,这个可视化面板是排查问题的一大利器。
不过 RabbitMQ 也有自己的短板:吞吐量在几款主流中间件里属于中等水平,单机大概几万条消息每秒,没有 Kafka 那种动辄百万级的处理能力;消息堆积的能力也偏弱,如果消费者长期跟不上,队列积压太深会导致性能明显下降。它更适合"业务系统之间的精细化消息路由",而不是"海量数据高速流转"。
在实际部署中,RabbitMQ 最常遇到的问题集中在安装和启动环节。Windows 上启动失败大部分是因为 Erlang 版本和 RabbitMQ 版本不匹配,或者主机名解析异常——RabbitMQ 启动时会尝试解析本机主机名,如果 hosts 文件里没有对应映射就会起不来。Linux 上用 Docker Compose 安装比较省心,但要注意把数据目录挂载到宿主机,否则容器删掉数据就全丢了。"rabbitmq 分配用户"也是高频需求,正确做法是创建独立的虚拟主机(vhost)和用户,再通过 set_permissions 命令授权,不要把默认的 guest 账号直接暴露给外部。
2.4 RocketMQ:电商交易场景打磨出来的国产中间件
RocketMQ 是阿里巴巴 2012 年开源的消息中间件,2016 年捐赠给 Apache 基金会。它的诞生背景是阿里内部对 ActiveMQ 的吞吐量和可靠性不满意,于是自己造了一个轮子,经过双十一大规模交易场景的验证后逐步开源。
RocketMQ 的定位非常明确:为交易类业务提供高可靠、低延迟、强顺序保障的消息服务。它的核心优势有三点。
第一是消息不丢失。RocketMQ 的同步刷盘 + 主从同步复制机制,保证消息写入 Broker 后即使机器宕机,消息也能从从节点恢复。这个特性对订单、支付、账户类业务至关重要——少一条消息可能就意味着一个订单状态不一致。第二是事务消息。RocketMQ 提供了分布式事务消息的支持,可以在"本地事务"和"消息发送"之间建立一致性保证,这是 RabbitMQ 和 Kafka 原生都不具备的能力。第三是消息顺序。RocketMQ 支持队列级别的顺序消息,同一个业务 ID 的消息会路由到同一个队列,消费时能严格按发送顺序处理。
除此之外,RocketMQ 支持四种消息类型:普通消息、顺序消息、延迟消息、事务消息。延迟消息的可配置延迟级别从 1 秒到 2 小时,比如下单后 30 分钟未支付自动关单,这类场景用 RocketMQ 的延迟消息实现非常方便。
RocketMQ 的架构里有一个容易让新手迷惑的点:它依赖 NameServer 做服务发现,而不是像 Kafka 那样依赖 ZooKeeper(4.0 之后 Kafka 也逐步去 ZK 了)。NameServer 是一个无状态的轻量级注册中心,Broker 启动后向 NameServer 注册自己的地址,生产者消费者从 NameServer 拉取路由信息。生产环境通常部署两个 NameServer 做高可用,两个或以上 Broker 组成主备。
在运维层面,RocketMQ 的命令行管理工具 mqadmin 用得非常多。创建 Topic 的命令是:
查询消费进度用:
"rocketmq 4.8.0 windows 安装"和"linux 安装 rocketmq 5.5.0"这类关键词经常被搜到,说明不少人在本地搭建环境时卡住了。RocketMQ 的 Windows 部署其实不难:从官网下载二进制包,配置好 JAVA_HOME,启动 NameServer 和 Broker 两个进程即可,但要注意 Broker 的配置文件 broker.conf 里需要设置 brokerIP1 为实际 IP,否则客户端可能连接不上。在 Linux 上部署 5.x 版本还要注意 JDK 版本要求,RocketMQ 5.x 对 JDK 8 和 JDK 11 都有兼容性支持,推荐使用 JDK 11。
2.5 Kafka:日志与流处理领域的王者
Kafka 的诞生背景和前面几个完全不同。LinkedIn 当时需要处理海量的用户行为日志,传统消息队列的吞吐量不够,于是他们设计了一个"分布式提交日志"——把所有消息当作不断追加的日志文件,消费者通过记录偏移量(Offset)来读取自己关心的那一段数据。
Kafka 的核心数据模型是 Partition(分区)。一个主题被拆成多个分区,每个分区是一个有序的、不可变的日志文件。消息追加到分区尾部,消费者从指定偏移量开始顺序读取。这种设计带来的直接收益是极高的写入吞吐量——消息写入是纯顺序追加磁盘,不需要随机寻址,单机就能支撑每秒几十万甚至上百万条消息的写入。
Kafka 在几个方向上有明显的不可替代性:
一是日志采集和传输。服务器日志、App 埋点数据、IoT 设备上报的海量时序数据,直接打到 Kafka 里做缓冲,再交给下游的 Flink、Spark 或数据仓库处理,这是 Kafka 最经典的用法。二是流式计算。Kafka 提供了 Kafka Streams 库,加上与 Flink、Spark Streaming 的深度集成,它已经不仅是消息队列,而是整个流处理架构的"中枢神经系统"。三是数据管道。Kafka Connect 可以方便地把 MySQL、PostgreSQL、Elasticsearch、HDFS 等系统与 Kafka 连接起来,实现数据在系统间流动。
但是 Kafka 也有明显的适用边界。它不支持事务消息,消息语义是至少一次(可以通过配置实现恰好一次,但开销大),没有延迟消息、死信队列这些业务消息中间件的概念。如果你用 Kafka 去处理订单支付,团队又对消息可靠性没有足够深的理解,很容易踩坑。
Kafka 的运维复杂度也高于 RabbitMQ 和 RocketMQ。它需要管理分区副本、ISR 集合、消费者组的再均衡,对 JVM 参数、磁盘性能、网络带宽都有较高要求。好消息是 Kafka 从 2.8 版本开始逐步移除对 ZooKeeper 的依赖,KRaft 模式让部署简单了不少,但社区生产环境仍然大量使用基于 ZK 的旧版本。
3. 横向对比:从消息模型到吞吐量、可靠性的全面对照
3.1 核心参数对比表
先把几个中间件的关键差异放在一张表里,后面再逐个展开讲:
| 维度 | EMQX | RabbitMQ | RocketMQ | Kafka |
|---|---|---|---|---|
| 本质 | MQTT Broker | 通用消息代理 | 业务消息中间件 | 分布式日志平台 |
| 核心协议 | MQTT | AMQP(兼MQTT等) | 自定义Remoting | 自定义TCP协议 |
| 消息模型 | 发布/订阅 | Exchange+Queue | Topic+Queue | Topic+Partition |
| 单机吞吐量 | 十万级 | 万级 | 十万级 | 百万级 |
| 消息可靠性 | 高(QoS 1/2) | 高(Publisher Confirm) | 极高(同步刷盘) | 高(副本机制) |
| 顺序消息 | 支持(主题内顺序) | 有限支持(单队列内) | 强支持(队列级别) | 强支持(分区内) |
| 延迟消息 | 不支持 | 插件支持 | 原生支持 | 不支持 |
| 事务消息 | 不支持 | 不支持 | 原生支持 | 不支持 |
| 管理界面 | 内置 Dashboard | 内置 Management UI | 需额外部署控制台 | 需第三方工具(KafkaUI 等) |
| 典型场景 | 物联网设备接入 | 企业系统解耦 | 电商交易 | 日志与流计算 |
表格里的数据是典型值,实际性能跟硬件配置、消息大小、副本数量关系很大,不要当作绝对指标去对比。我见过有人在 4 核 8G 的机器上测 Kafka,结果被 RabbitMQ 吊打,这只能证明测试环境不合理,不能说明产品不行。
3.2 消息模型差异:队列、发布订阅与分区日志
理解消息中间件的核心,先看它们的消息模型。
RabbitMQ 和 RocketMQ 走的是队列模型的变种。生产者把消息发到队列,消费者从队列里取,一条消息被消费后就从队列中移除。RocketMQ 在这个基础上变成了"每个主题下挂多个消费队列",同一个消费组内的消费者分摊不同队列,实现水平扩展。
Kafka 走的是分区日志模型。消息写入分区后不会删除,消费者通过 Offset 记录自己读到哪儿了,就像看一本永远写不完的书,你只需要记得自己看到第几页。消息的保留策略是时间或者大小,跟消费与否无关。这个设计带来的好处是消费者可以反复读数据,比如接入两个新的数据管道时,它们可以从头开始消费历史数据,不需要改任何上游配置。
EMQX 和 MQTT 协议走的是纯发布/订阅模型。消息发布到主题后,Broker 负责找到所有匹配的订阅者并转发,消息不会被存储(或者说存储不是核心能力)。EMQX 5.x 开始支持消息持久化和消息重放,但那是通过底层内置的存储引擎实现的,跟 MQTT 协议本身无关。
这三种模型的本质区别决定了它们的适用场景:业务系统间的命令/事件传递适合队列模型,海量数据的持续流转适合日志模型,短小频繁的设备消息适合发布/订阅模型。
3.3 可靠性与性能的权衡
很多人在选型时盯着吞吐量不放,这是一个误区。消息中间件的可靠性、实时性、吞吐量三者之间存在天然的取舍。
以 Kafka 为例,它能达到百万级吞吐,前提是异步批量写入、批量复制;但如果要求每条消息都同步落盘并等待所有副本确认,吞吐量会显著下降。Kafka 提供了 acks=0、acks=1、acks=all 三个可靠性等级,就是让使用者在性能和可靠性之间做选择。我见过很多团队把 acks=all 开起来后觉得 Kafka "变慢了",其实不是 Kafka 慢了,而是你选择了更强的可靠性,自然要付出代价。
RocketMQ 在可靠性上走得更极端,它支持同步双写和同步刷盘,这保证了消息不会因为机器宕机而丢失,但单机吞吐量上限就低于 Kafka。如果你们公司的业务是交易类的,那么"慢一点但是绝对不丢"比"快但可能丢"重要得多。
RabbitMQ 的设计哲学是"功能丰富、路由灵活",性能不是它的主打项。它的消息确认机制(Publisher Confirm + Consumer Ack)做得很完善,配合死信队列和延迟消息插件,适合处理复杂的业务路由。但它的存储机制导致消息堆积后性能下降很快——官方自己都建议队列中堆积的消息数不要超过一定的水位线,超过后要及时处理。
EMQX 的场景比较特殊,它面向的是海量长连接设备,性能指标重点是连接数和消息转发延迟,而不是磁盘吞吐。一个 EMQX 节点挂 100 万台设备,每台设备每 10 秒发一条消息,综合 TPS 也就 10 万,这个量级对 EMQX 来说压力不大,真正的挑战在连接管理、会话保持和弱网兜底。
4. 选型实践:不同业务场景下怎么选
4.1 物联网设备接入:MQTT 协议 + EMQX
如果你们的业务涉及大量设备接入——智能家居、车联网、工业传感器、充电桩、环境监测——首选方案几乎可以无脑定为 MQTT + EMQX。
为什么不用 RabbitMQ 或 Kafka?先说 RabbitMQ,它虽然通过插件支持 MQTT,但并发连接能力和弱网处理能力远不如专门的 MQTT Broker。再说 Kafka,它的吞吐量虽然高,但 MQTT 协议的 QoS、遗嘱消息、持续会话这些物联网核心特性它都不支持,强行对接需要自己写一层协议转换,还丢了设备的实时在线状态管理。
具体方案上,工业设备如 PLC、RTU 通常通过 4G 模块或网关接入,网关内部跑 MQTT 协议;如果设备不支持 MQTT,也可以用 Node-RED、KepServer 这类工具做协议转换,比如"Node-RED 实现 OPC UA 转 MQTT"就是我见过很多工业项目在用的桥接路径。设备数据到达 EMQX 后,通过规则引擎清洗、过滤,再转发到业务后端或者 Kafka。如果设备是通过单片机直接上报,就需要在 STM32、ESP32 上移植 MQTT 客户端库,通常在 FreeRTOS 上配合 lwIP 协议栈实现 TCP 连接,再调用 MQTT 库的 API 完成连接、订阅、发布。这部分工作 EMQX 不参与,它只管接收消息。
部署架构上,中小项目单节点 EMQX 就够用,生产环境至少两台机器做集群,前端挂负载均衡。EMQX 集群配置非常简单,节点之间会自动发现并同步路由表,不需要像 Kafka 那样需要额外的协调服务。连接数、消息量再大的场景,可以按区域拆成多个集群,用桥接或者共享订阅做跨集群消息同步。
4.2 企业内部系统解耦:RabbitMQ 是稳妥之选
如果场景是企业内部多个系统之间的异步调用、事件通知、任务分发,RabbitMQ 的优雅之处在于它几乎不挑场景。
用户注册后需要发邮件、发短信、送积分、通知 CRM 系统——用 RabbitMQ 的 Topic 交换机,各种绑定规则都能清晰表达。后台任务分发、图片处理、报表生成,用 RabbitMQ 的 Work Queue 模型加手动 ACK,能保证任务不丢、失败重试。RabbitMQ 还有一个杀手级优势:管理界面太友好了。队列堆积了多少、哪些消费者掉线了、消息是否被拒收,运维人员打开页面就能看到,不需要依赖命令行核查。
RabbitMQ 的另一个亮点是插件生态。延迟消息插件(rabbitmq_delayed_message_exchange)可以解决延时任务场景,MQTT 插件可以让它同时充当物联网网关,联邦插件(Federation)和铲子插件(Shovel)可以搭建跨机房的消息同步。这些能力大部分开箱即用,生产环境部署时通过 rabbitmq-plugins 命令一键启用:
使用 RabbitMQ 时最容易忽视的坑是连接复用。很多人写代码时每条消息都创建一个新连接,连接建立和销毁的开销非常大,生产环境一压测就会出现连接风暴。正确做法是使用连接池,每个应用实例只需维持少量长连接,在连接上复用 Channel 做消息发送和接收。
4.3 电商交易与订单处理:RocketMQ 更适合
订单、支付、积分、库存这类业务,消息的价值是**"绝对可靠"**。你希望一条订单状态变更的消息绝对不丢、严格有序,并且能通过事务机制保证数据库和消息的一致性。这时候 RocketMQ 的竞争力就体现出来了。
我之前提到的事务消息,这里展开说。所谓分布式事务消息,解决的是"本地数据库操作和发消息"之间的原子性问题。RocketMQ 的实现流程是:生产者先发送"半消息",Broker 收到后暂存且不让消费者看到;生产者执行本地事务(比如写入订单表);事务执行成功后向 Broker 提交确认,消息才对消费者可见。如果本地事务执行失败,Broker 会回查生产者的事务状态,要么回滚消息,要么继续投递。这套机制让消息发送和业务操作变成了一个"近似原子"的过程,对电商交易这种强一致场景非常重要。
另一个高频需求是延迟消息。"下单 30 分钟未支付自动关单""支付失败后 5 分钟自动重试提醒""会员到期前 3 天发送续费通知",这些场景用 RocketMQ 的延迟消息实现非常顺手。注意 RocketMQ 的延迟消息不支持任意时间,而是预设好的 18 个等级,从 1 秒到 2 小时。如果想支持任意延迟时间,需要修改 Broker 配置或者自己实现定时调度。
如果团队技术栈主要基于 Java,用 RocketMQ 还有一个额外的好处:官方客户端功能完善,事务消息 API、顺序消息 API、延迟消息 API 都有非常清晰的调用方式,Spring Boot 集成也比较成熟。而 RabbitMQ 的 Java 客户端虽然稳定,但在事务、顺序、延迟这些高级特性上需要自己拼装。
4.4 日志采集、大数据管道与流计算:Kafka 没有对手
当数据量达到一定程度,消息中间件的选型就被数据规模推向了 Kafka。
典型的场景:几十台服务器的业务日志和应用日志,通过 Filebeat、Logstash 或 Fluentd 采集后写入 Kafka,再交给 Logstash 或 Flink 做解析处理,最后写入 Elasticsearch 供 Kibana 查询。另一个常见场景是做用户行为分析:前端埋点数据、App 点击流数据量级非常大,Kafka 天然的缓冲能力可以让后端消费者按照自己的节奏处理,不会因为某个时段流量突增而挂掉。
Kafka 在数据管道场景中的角色已经超出了一般消息中间件的范畴。Kafka Connect 提供了各种 Source Connector 和 Sink Connector,比如把 MySQL 的 binlog 同步到 Kafka,再把 Kafka 里的数据写入数据仓库、搜索引擎或数据湖。这种模式叫 Change Data Capture(CDC),是很多数据中台项目的核心架构。
如果你们公司已经引入了 Flink、Spark Structured Streaming,Kafka 更是绕不开的——因为流计算框架和 Kafka 的集成是最顺畅的。Kafka 作为数据源和结果汇,配合 Flink 做状态管理、窗口计算,几乎成了实时数仓的标准姿势。
需要提醒的是,Kafka 的消费者组机制虽然好用,但新手经常弄混 Offset 的管理。消费组中的每个消费者负责一组分区,某个消费者掉线后会触发 Rebalance,把它的分区重新分配给其他消费者。Rebalance 期间消费会短暂中断,如果频繁发生,通常是消费者处理耗时过长或心跳超时,需要调大 max.poll.interval.ms 或者增加消费者实例数。
5. 常见问题与排查经验实录
5.1 安装与启动类问题
消息中间件在本地环境安装时坑最多,我把高频问题整理成一张表:
| 现象 | 常见原因 | 解决思路 |
|---|---|---|
| RabbitMQ 在 Windows 启动失败 | Erlang 版本不匹配或主机名解析失败 | 检查 Erlang 与 RabbitMQ 版本对应表,把主机名和 IP 加到 hosts 文件 |
| RabbitMQ 管理界面打不开 | 未启用 management 插件 | 执行 rabbitmq-plugins enable rabbitmq_management 后重启 |
| RocketMQ 启动 NameServer 成功但 Broker 失败 | 内存不足或 brokerIP1 配置不对 | Broker 默认 JVM 参数较高,改成 -Xms256m -Xmx256m;配置 brokerIP1 为本机 IP |
| RocketMQ 客户端连接超时 | 防火墙未开 9876 和 10911 端口 | 安全组开放端口,确认 brokerIP1 配置为客户端可达地址 |
| EMQX 启动后 Dashboard 无法访问 | 端口被占用或只监听了内网 IP | 确认 18083 端口未被占用,检查监听地址配置 |
| Windows 下同时装有多个消息中间件导致端口冲突 | 多个服务默认端口相同 | 合理规划端口,RabbitMQ 用 5672、RocketMQ 用 9876/10911、Kafka 用 9092,EMQX 用 1883 |
RabbitMQ 的 Windows 部署是最多人踩坑的。很多人从官网直接下载了最新版 RabbitMQ,却忽略了 Erlang 版本的对应关系。RabbitMQ 每个版本对 Erlang 版本都有明确的兼容区间,两者版本不匹配时服务会直接闪退,或者启动后立刻退出。检查日志时如果看到 Failed to start Ranch listener 这类错误,十有八九是版本问题。解决方案就是去官网查看 Compatibility Matrix,下载匹配的 Erlang 版本。
RocketMQ 在 Linux 上安装时遇到最多的问题是启动脚本内存参数过大。默认 Broker 启动脚本里分配了 8G 堆内存,个人电脑或者 4G 内存的云服务器根本跑不动。改一下 runbroker.sh 里的 JAVA_OPT 和 runserver.sh 里的参数,把 Xms 和 Xmx 调小,再启动就正常了。另外,RocketMQ 默认只监听内网地址,如果要在云服务器上给外部客户端使用,必须在 broker.conf 里追加 brokerIP1=服务器公网IP。
5.2 客户端连接与使用问题
连接问题是日常使用中最多的一类。
用 MQTTX 连不上 EMQX,先检查三件事:Broker 地址和端口是否填对(默认 1883 端口,走 WebSocket 是 8083)、用户名密码是否正确(EMQX 默认允许匿名但生产环境应关闭)、是否有防火墙拦截。如果连接后反复断开,看一下是不是客户端 ID 冲突——MQTT 协议规定同一个 Client ID 只能保持一个会话,你在 MQTTX 开了两个窗口用了相同的 Client ID,后面的连接会把前面的踢掉。
前端项目接 MQTT 也是高频需求。Vue 项目通常用 mqtt.js 库,通过 WebSocket 连接 EMQX。这里有个容易忽略的点:浏览器环境的 WebSocket 连接必须走 EMQX 的 8083 端口,而不是 1883 端口。很多人在浏览器里直接连 1883 失败,就是因为没分清 TCP 连接和 WebSocket 连接的区别。连接地址应该写成 ws://服务器IP:8083/mqtt,注意路径里的 /mqtt 不能省略。
RabbitMQ 的前端访问问题通常是权限配置不合理。默认情况下 guest 账号只能在 localhost 使用,远程访问必须在管理界面新建用户并授予权限。需要给前端用的账号,建议单独建一个只读用户,授予特定 vhost 的 configure、write、read 权限,不要直接把 admin 账号给前端用。设置权限的命令:
前端通过 STOMP over WebSocket 连接 RabbitMQ 时,要注意路径和端口。RabbitMQ 的 Web STOMP 插件默认监听 15674 端口,需要先启用插件。连接成功后进行订阅时,订阅地址要跟后端绑定的队列名完全一致,否则消息到了 RabbitMQ 之后就"消失"了。
5.3 消息丢失、重复与堆积排查
消息丢失是消息中间件使用中最严重的问题,排查思路一般是先确定丢在哪个环节。
生产者到 Broker 这一段丢失:Kafka 检查生产者是否设置了 acks=all,RocketMQ 检查是否启用了同步发送和同步刷盘,RabbitMQ 检查是否启用了 Publisher Confirm。如果生产者发送时没有确认机制,Broker 接收失败或者落盘失败时生产者完全不知道,消息就凭空消失了。
Broker 到消费者这一段丢失:RabbitMQ 的消费者必须开手动 ACK,默认自动 ACK 模式下消费者收到消息但还没处理完进程就挂了,消息就丢了;Kafka 消费者要注意关闭自动提交 Offset,改为处理完业务逻辑后再手动提交,否则进程重启后会跳过一批未处理的消息。
消息重复则是另一个常见问题。几乎所有消息中间件在不做特殊处理的情况下都是"至少一次"语义,网络超时重发、Broker 切换、消费者处理成功但 ACK 丢失,都会导致消费者收到重复消息。这不是选型能解决的,而是业务侧必须做幂等处理。我见过很多团队花大量精力去调中间件参数希望实现"恰好一次",最后发现收效甚微,因为重复的根本原因在网络和分布式系统的不可靠性。正确做法是在消费端通过唯一业务 ID 做去重,例如数据库唯一索引、Redis SETNX,或者状态机校验。
消息堆积的诊断思路一般是这样的:先看消费速率和生产速率,用管理界面或者命令行查询消费组的 Lag。如果 Lag 持续增长,说明消费速度跟不上生产速度,可能的原因有几个——消费者实例太少,处理逻辑太慢(比如每条消息都要调用外部接口),或者消费者已经挂了但没有触发重平衡。处理方式通常是先扩容消费者实例,再看有没有慢消费的日志;如果是下游依赖慢,考虑引入批量处理或者限流降级。
5.4 高可用与扩展的常见误区
很多团队做消息中间件选型时只关注功能,忽略了高可用部署的复杂度。
RabbitMQ 的高可用依赖于镜像队列(classic mirroring)或仲裁队列(quorum queue)。新手常犯的错误是只搭了集群没配置队列复制,结果队列只存在于某个节点上,那个节点挂了队列就不可用。仲裁队列是 RabbitMQ 3.8 之后推荐的方案,基于 Raft 协议自动实现多副本复制,比镜像队列更稳定,建议新项目直接使用。
Kafka 的高可用主要靠分区副本,需要把副本因子设成 3,并且确认 ISR 中至少有一个跟随者同步完成。很多团队图省事把副本因子设成 1,节点一挂数据全丢。另外 broker 的 unclean.leader.election.enable 参数要谨慎设置,开启后虽然能在主副本完全丢失时恢复服务,但可能造成消息丢失,在注重可靠性的场景建议关闭。
EMQX 的集群配置相对简单,但要注意两个点:第一,集群内的所有节点必须使用相同的 Cookie,否则节点之间无法互相通信;第二,生产环境一定要用真实主机名或 IP 配置节点名,不要用 localhost,否则集群通信会失败。EMQX 集群的扩容也需要注意,不要在高峰期直接加节点,最好在低峰期操作并确保负载均衡策略正确。
RocketMQ 在阿里生产环境的部署标准是双主双从,Broker 主从结构配合同步复制可以保障很高的可用性。但要注意,RocketMQ 的消费进度存储在 Broker 端而不是消费者端,机器部署时要确保 Broker 的磁盘空间足够,消息堆积会导致本地 CommitLog 文件膨胀,磁盘满了之后服务会拒绝写入。
6. 我的实操心得与避坑经验
文章的最后一个部分,分享一些我个人在实际项目中积累的判断和经验,不保证适合所有团队,但大概率能帮你少走一些弯路。
第一个心得是:不要在项目一开始就追求"引入最牛的中间件"。我见过不少团队,业务量每天只有几万条消息,却在生产环境搭了一套 Kafka 集群加 Flink 实时计算,最后发现维护成本远超收益。选型的第一原则是匹配业务阶段——先问清楚场景是什么、数据量级是多少、可靠性要求多高,再谈用哪个。中小团队初期用 RabbitMQ 或 RocketMQ 单机部署足够,等规模大了再迁移到 Kafka 或者扩集群,不会有什么沉没成本。
第二个心得是:一定要重视监控和告警。消息中间件是系统的"血管",血管堵了全链路都会出问题。RabbitMQ 可以通过管理界面直接看到队列堆积情况,RocketMQ 可以用 mqadmin 查看消费进度,Kafka 有 Kafka Lag Exporter 之类的工具,EMQX 的 Dashboard 也有实时指标。无论用哪个,至少要监控这几个指标:消息堆积量(Lag)、消费速率、连接数、Broker 所在机器的磁盘和内存。告警规则宁多勿少,消息中间件的故障通常从堆积开始,堆积能从 0 涨到几百万,中间有足够的时间介入,关键是要有人看告警。
第三个心得是:消息中间件的版本升级要谨慎。RabbitMQ 升级到新大版本时,很多插件、队列类型、协议行为可能发生变化;Kafka 从 2.x 升到 3.x 时,不少参数和客户端兼容性要求都变了;RocketMQ 从 4.x 升到 5.x,部署架构和客户端 API 都有调整。我在生产环境执行过消息中间件的滚动升级,每一次都做了充分的兼容性测试才动线上,这个环节不能急。
最后分享一个实用的小技巧:调试消息中间件问题时,先用最简单的工具把数据链路打通,再逐步替换系统中的复杂组件。比如排查 EMQX 收不到消息的问题,不要直接翻业务代码,先用 MQTTX 手动连接 EMQX 发一条消息,看 Dashboard 里流量有没有变化;如果 MQTTX 能发通,问题就在业务客户端或者网络环境;如果 MQTTX 都不通,再去检查 EMQX 的配置和防火墙。这种"先小后大、先通后优"的排查思路,适用于所有消息中间件的故障处理。
消息中间件的体系很大,每个组件单拿出来都能写一本书。这篇文章的目标是帮你理清"MQTT、EMQX、RabbitMQ、RocketMQ、Kafka 到底是什么关系、各自擅长什么、什么场景选哪个",把这些基础打牢之后,深入某个具体中间件时就不容易走偏了。