← 返回业务系统实战

📨 消息队列选型实战

Kafka vs RocketMQ vs RabbitMQ — 什么时候用哪个 · 代码实例 · 业务系统对照 · 优缺点陷阱

Kafka RocketMQ RabbitMQ 选型决策 代码实例 业务场景

1. 先搞清楚:为什么需要消息队列?

消息队列不是"为了用而用",它解决三大问题:解耦、异步、削峰

能力不用 MQ 的痛用 MQ 之后
解耦订单系统直接调短信/积分/库存服务,任何下游挂了订单就失败订单发消息到 MQ,下游各自消费,谁挂了不影响下单
异步用户下单要等短信+积分+库存全部完成才返回,2 秒+下单同步写库 + 发 MQ,50ms 返回,下游异步处理
削峰秒杀 10 万请求直接打到 DB,瞬间雪崩请求先进 MQ 排队,消费端按 DB 承受能力匀速消费
但 MQ 不是银弹:引入 MQ 会带来系统复杂度 + 一致性挑战 — 消息丢失、重复消费、顺序问题、积压、可用性下降。所以能用同步解决就别上 MQ,只有当解耦/异步/削峰的收益大于复杂度成本时才用。

2. 三者速览 — 一句话定位

🔥 Kafka

"大数据的高速公路"
  • 出身:LinkedIn 开源,Apache 顶级项目
  • 定位:分布式流处理平台,日志/事件流
  • 吞吐:百万级 TPS,磁盘顺序写
  • 延迟:毫秒级
  • 语言:Scala + Java
  • 典型场景:日志采集、大数据管道、流计算

🚀 RocketMQ

"电商的事务专家"
  • 出身:阿里开源,Apache 顶级项目
  • 定位:分布式消息中间件,参考 Kafka 设计
  • 吞吐:十万级 TPS
  • 延迟:毫秒级
  • 语言:Java
  • 典型场景:电商交易、订单、事务消息

🐰 RabbitMQ

"灵活的路由小能手"
  • 出身: Erlang 实现,AMQP 协议
  • 定位:传统消息代理,强调路由灵活性
  • 吞吐:万级 TPS
  • 延迟:微秒级(最快)
  • 语言:Erlang
  • 典型场景:业务消息、复杂路由、RPC
类比 Kafka = 高速公路(吞吐大、单向、批量发车)· RocketMQ = 快递公司(有事务、有延迟投递、要签收回执)· RabbitMQ = 邮局(路由最灵活、能挂号信、能转寄、单件投递)

3. 核心能力对比表(选型核心依据)

维度KafkaRocketMQRabbitMQ
吞吐量百万级 TPS十万级 TPS万级 TPS
延迟毫秒级毫秒级微秒级(最快)
消息顺序分区有序分区有序单队列有序
事务消息弱(0.11+ 有但少用)原生支持(强项)
消息回溯按 offset 任意回放按时间回放不支持(消费即删)
定时/延迟消息无原生支持原生支持(18个级别)插件支持
路由能力弱(Topic 级)中(Tag + SQL)极强(Exchange 4种)
消息堆积极强(磁盘)强(磁盘)弱(内存为主,堆积性能陡降)
可用性高(副本)高(主从同步)高(镜像队列)
客户端语言全(Java/Go/Python...)主要 Java全(AMQP 标准)
运维复杂度中(Zookeeper/KRaft)低(NameServer 轻量)低(开箱即用)
消息丢失风险需配置 acks=all默认同步刷盘需配置持久化
典型用户LinkedIn/Netflix/Uber/字节阿里/京东/滴滴/小米Twitter/Reddit/Steam
核心差异一句话:Kafka 强在吞吐和回溯(大数据场景);RocketMQ 强在事务和延迟消息(电商交易场景);RabbitMQ 强在路由灵活和低延迟(业务消息场景)。

4. Kafka 实战 — 日志采集 + 大数据管道

场景:用户行为日志采集

前端埋点上报 → Kafka → Flink 实时计算 + HDFS 离线分析。日均 100 亿条日志,Kafka 单机扛 10 万 TPS 不掉队。

📋 Kafka 生产者 + 消费者完整代码(Java) 点击展开
项目结构
📦 kafka-demo
📁 src
📁 main
📁 producer
LogProducer.java
📁 consumer
LogConsumer.java
application.yml
LogProducer.java
// ===== Kafka 生产者:发送用户行为日志 ===== import org.apache.kafka.clients.producer.*; public class LogProducer { public static void main(String[] args) { // 1. 生产者配置(关键参数都加了注释) Properties props = new Properties(); props.put("bootstrap.servers", "kafka1:9092,kafka2:9092,kafka3:9092"); props.put("key.serializer", "org.apache.kafka.common.serialization.StringSerializer"); props.put("value.serializer", "org.apache.kafka.common.serialization.StringSerializer"); // ⚠️ acks=all:所有副本都写入才返回成功,保证消息不丢 props.put("acks", "all"); // ⚠️ 重试次数:网络抖动时自动重试 props.put("retries", 3); // ⚠️ 批量发送:提升吞吐,攒一批再发 props.put("batch.size", 16384); props.put("linger.ms", 10); // ⚠️ 幂等生产者:防止重试导致消息重复 props.put("enable.idempotence", "true"); try (KafkaProducer<String, String> producer = new KafkaProducer<>(props)) { for (int i = 0; i < 100; i++) { // 2. 构造日志消息:JSON 格式 String log = "{\"userId\":\"u" + i + "\",\"action\":\"click\",\"ts\":1700000000}"; // 3. 用 userId 做 key → 相同用户进同一分区,保证用户级行为有序 ProducerRecord<String, String> record = new ProducerRecord<>("user-behavior-log", "u" + i, log); // 4. 异步发送 + 回调(失败要记录,不能吞掉) producer.send(record, (metadata, e) -> { if (e != null) { System.err.println("发送失败: " + e.getMessage()); // TODO: 落库重发 / 死信队列 } else { System.out.printf("offset=%d, partition=%d%n", metadata.offset(), metadata.partition()); } }); } producer.flush(); // 强制把缓冲区消息发出去 } } } // ===== Kafka 消费者:消费日志写入 HDFS/Flink ===== public class LogConsumer { public static void main(String[] args) { Properties props = new Properties(); props.put("bootstrap.servers", "kafka1:9092"); props.put("group.id", "log-hdfs-consumer-group"); props.put("enable.auto.commit", "false"); // ⚠️ 关自动提交 props.put("key.deserializer", "org.apache.kafka.common.serialization.StringDeserializer"); props.put("value.deserializer", "org.apache.kafka.common.serialization.StringDeserializer"); // ⚠️ earliest:从头消费;latest:只消费新消息(默认) props.put("auto.offset.reset", "earliest"); try (KafkaConsumer<String, String> consumer = new KafkaConsumer<>(props)) { consumer.subscribe(Arrays.asList("user-behavior-log")); while (true) { // 1. 拉取消息(批量),超时 1 秒 ConsumerRecords<String, String> records = consumer.poll(Duration.ofMillis(1000)); for (ConsumerRecord<String, String> record : records) { try { // 2. 处理消息:写 HDFS / 喂 Flink processLog(record.value()); } catch (Exception e) { // ⚠️ 处理失败不能提交 offset,下次会重投 System.err.println("处理失败,稍后重试: " + e.getMessage()); return; } } // 3. 手动提交 offset(处理成功后才提交) consumer.commitSync(); } } } }
Kafka 优点
  • 吞吐量碾压(百万级 TPS)
  • 消息可回溯(offset 任意位置重消费)
  • 生态强大(Connect/Streams/Flink 对接)
  • 水平扩展简单(加 broker 即可)
  • 持久化到磁盘,堆积不影响性能
Kafka 缺点
  • 无事务消息(0.11+ 有但少用)
  • 无延迟消息/定时消息
  • 路由能力弱(只有 Topic 维度)
  • 运维依赖 Zookeeper(KRaft 模式在改善)
  • 单分区有序,跨分区无序
  • 消息无 TTL 概念(按 retention 删)

5. RocketMQ 实战 — 电商订单 + 事务消息

场景:电商下单的分布式事务

用户下单:要扣库存 + 扣积分 + 发短信。三个服务跨库跨系统,无法用本地事务。用 RocketMQ 事务消息保证"本地事务 + 消息发送"原子性 — 这是 RocketMQ 的杀手锏。

📋 RocketMQ 事务消息完整代码(下单场景) 点击展开
事务消息
📦 order-service
📁 src
📁 service
OrderService.java
📁 listener
OrderTransactionListener.java
📁 consumer
StockConsumer.java
OrderService.java — 发送事务消息
// ===== 下单服务:用事务消息保证"扣库存 + 发消息"原子性 ===== @Service public class OrderService { @Autowired private TransactionMQProducer transactionProducer; public void createOrder(OrderDTO order) throws Exception { // 1. 构造消息:Topic = order-topic, Tag = create Message msg = new Message( "order-topic", // Topic "create", // Tag(子分类,可按 Tag 订阅) order.getUserId(), // Key(用于查询/幂等) JSON.toJSONString(order).getBytes() // body ); // 2. 发送事务消息(半消息)— 这一步会先发到 MQ,但消费者看不到 TransactionSendResult result = transactionProducer.sendMessageInTransaction( msg, new OrderTransactionListener() // 事务监听器 ); if (result.getLocalTransactionState() == LocalTransactionState.COMMIT_MESSAGE) { System.out.println("事务提交,消息可被消费"); } else { System.out.println("事务回滚,消息会被删除"); } } } // ===== 事务监听器:执行本地事务 + 回查 ===== public class OrderTransactionListener implements TransactionListener { @Autowired private OrderMapper orderMapper; // 本地数据库 // ① 执行本地事务(半消息发送成功后回调) @Override public LocalTransactionState executeLocalTransaction(Message msg, Object arg) { try { OrderDTO order = JSON.parseObject(new String(msg.getBody()), OrderDTO.class); // 执行本地事务:写订单表 + 扣库存 orderMapper.insert(order); stockService.deduct(order.getProductId(), order.getQty()); // 本地事务成功 → 提交消息,消费者能看到 return LocalTransactionState.COMMIT_MESSAGE; } catch (Exception e) { // 本地事务失败 → 回滚消息,消息删除 return LocalTransactionState.ROLLBACK_MESSAGE; } } // ② 事务回查(MQ 主动问:你那个事务到底成没成?) @Override public LocalTransactionState checkLocalTransaction(MessageExt msg) { // ⚠️ 如果 executeLocalTransaction 超时/挂了,MQ 会回查 String orderId = msg.getKeys(); Order order = orderMapper.selectById(orderId); if (order != null) { // 订单已落库 → 事务成功 → 提交 return LocalTransactionState.COMMIT_MESSAGE; } // 订单不存在 → 事务未完成 → 回滚 return LocalTransactionState.ROLLBACK_MESSAGE; } } // ===== 库存消费者:消费订单消息,扣库存 ===== @Component @RocketMQMessageListener(topic = "order-topic", consumerGroup = "stock-consumer-group") public class StockConsumer implements RocketMQListener<OrderDTO> { @Override public void onMessage(OrderDTO order) { // ⚠️ 幂等性:防止重复消费扣两次库存 boolean isNew = redis.setIfAbsent( "order:processed:" + order.getId(), "1", 24, HOURS ); if (!isNew) return; // 已处理过,跳过 stockService.deduct(order.getProductId(), order.getQty()); } }
📋 RocketMQ 延迟消息代码(订单超时自动取消) 点击展开
延迟消息
📦 order-service
📁 service
OrderTimeoutService.java
📁 consumer
OrderCancelConsumer.java
OrderTimeoutService.java
// ===== 场景:下单 30 分钟未支付,自动取消订单 ===== @Service public class OrderTimeoutService { @Autowired private DefaultMQProducer producer; public void sendDelayMessage(String orderId) throws Exception { Message msg = new Message( "order-timeout-topic", "timeout", orderId, orderId.getBytes() ); // ⚠️ RocketMQ 延迟级别:1s 5s 10s 30s 1m 2m 3m 4m 5m 6m 7m 8m 9m 10m 20m 30m 1h 2h // 30 分钟对应级别 16 msg.setDelayTimeLevel(16); // 30 分钟后投递 producer.send(msg); // 发送后,MQ 会在 30 分钟后才把消息投递给消费者 } } // ===== 消费者:30 分钟后收到,检查支付状态,未支付就取消 ===== @Component @RocketMQMessageListener(topic = "order-timeout-topic", consumerGroup = "order-cancel-group") public class OrderCancelConsumer implements RocketMQListener<String> { @Override public void onMessage(String orderId) { Order order = orderMapper.selectById(orderId); if (order.getStatus() == OrderStatus.UNPAID) { // 仍未支付 → 取消订单 + 回滚库存 orderMapper.updateStatus(orderId, OrderStatus.CANCELLED); stockService.rollback(order.getProductId(), order.getQty()); } // 已支付 → 忽略 } }
RocketMQ 优点
  • 原生事务消息(电商必备)
  • 延迟消息(18 个级别)
  • 消息可回溯(按时间)
  • Java 生态友好(开源 + 文档)
  • 单机支持 10 万 TPS,够用
  • NameServer 轻量,运维简单
RocketMQ 缺点
  • 非 Java 生态客户端少
  • 社区不如 Kafka 活跃
  • 路由能力不如 RabbitMQ(无 Exchange)
  • 延迟级别固定(不能任意时间)
  • 海外用得少(主要国内)

6. RabbitMQ 实战 — 业务消息 + 灵活路由

场景:订单事件多路分发

订单创建后要通知:短信服务、邮件服务、数据分析服务、库存服务。每个服务关心不同的事件,用 RabbitMQ 的 Exchange + Routing Key 实现灵活路由 — 这是 RabbitMQ 的强项。

📋 RabbitMQ 生产者 + 消费者完整代码(Spring Boot) 点击展开
RabbitMQ
📦 order-service
📁 config
RabbitConfig.java
📁 producer
OrderEventProducer.java
📁 consumer
SmsConsumer.java
EmailConsumer.java
RabbitConfig.java — Exchange + Queue + Binding
// ===== RabbitMQ 配置:Topic Exchange + 多队列绑定 ===== @Configuration public class RabbitConfig { public static final String ORDER_EXCHANGE = "order.exchange"; public static final String SMS_QUEUE = "order.sms.queue"; public static final String EMAIL_QUEUE = "order.email.queue"; public static final String ANALYSIS_QUEUE = "order.analysis.queue"; // 1. 声明 Topic Exchange(按 routing key 模式匹配) @Bean public TopicExchange orderExchange() { return new TopicExchange(ORDER_EXCHANGE, true, false); // durable=true 持久化 } // 2. 声明队列(durable=true 持久化,防重启丢消息) @Bean public Queue smsQueue() { return QueueBuilder.durable(SMS_QUEUE).build(); } @Bean public Queue emailQueue() { return QueueBuilder.durable(EMAIL_QUEUE).build(); } @Bean public Queue analysisQueue() { return QueueBuilder.durable(ANALYSIS_QUEUE).build(); } // 3. 绑定:不同 routing key 路由到不同队列 @Bean public Binding smsBinding() { // order.created.* → 匹配 order.created.sms / order.created.any return BindingBuilder.bind(smsQueue()).to(orderExchange()).with("order.created.*"); } @Bean public Binding emailBinding() { return BindingBuilder.bind(emailQueue()).to(orderExchange()).with("order.created.*"); } @Bean public Binding analysisBinding() { // order.# → 匹配 order.created / order.paid / order.cancelled 所有事件 return BindingBuilder.bind(analysisQueue()).to(orderExchange()).with("order.#"); } } // ===== 生产者:发送订单事件 ===== @Service public class OrderEventProducer { @Autowired private RabbitTemplate rabbitTemplate; public void sendOrderCreated(Order order) { // ⚠️ 关键配置 1:消息持久化 MessagePostProcessor persistence = msg -> { msg.getMessageProperties().setDeliveryMode(MessageDeliveryMode.PERSISTENT); return msg; }; // ⚠️ 关键配置 2:发布确认(确保消息到了 MQ) rabbitTemplate.setConfirmCallback((correlationData, ack, cause) -> { if (!ack) { System.err.println("消息未到达 Exchange: " + cause); // TODO: 重发 / 落库补偿 } }); // ⚠️ 关键配置 3:return 回调(Exchange 到不了 Queue 时触发) rabbitTemplate.setReturnsCallback(returned -> { System.err.println("消息无法路由: " + returned.getReplyText()); }); // 发送:routing key = order.created.sms rabbitTemplate.convertAndSend( RabbitConfig.ORDER_EXCHANGE, "order.created.sms", order, persistence ); } } // ===== 消费者:短信服务(手动 ACK) ===== @Component public class SmsConsumer { @RabbitListener(queues = RabbitConfig.SMS_QUEUE) public void handle(Order order, Channel channel, long tag) throws IOException { try { // 业务处理:发短信 smsService.send(order.getUserPhone(), "您的订单已创建"); // ⚠️ 手动 ACK:处理成功才确认 channel.basicAck(tag, false); } catch (Exception e) { // ⚠️ 处理失败:requeue=false 进死信队列,不重新排队 channel.basicNack(tag, false, false); } } }
RabbitMQ 优点
  • 路由极灵活(4 种 Exchange 模式)
  • 延迟最低(微秒级)
  • 开箱即用,运维简单
  • 客户端语言丰富(AMQP 标准)
  • 管理界面友好(Web UI)
  • 死信队列 + TTL 原生支持
RabbitMQ 缺点
  • 吞吐量最低(万级)
  • 消息堆积性能陡降(内存为主)
  • 无消息回溯(消费即删)
  • 无事务消息
  • Erlang 运维门槛(出问题难调)
  • 集群扩展不如 Kafka

7. 业务系统对照 — 你的业务该用哪个?

结合全栈资料库里 9 大业务系统,逐一对照"该用哪个 MQ"。

🛒 订单系统 RocketMQ

下单涉及多服务(库存/积分/短信),需要事务消息保证一致性;订单超时取消需要延迟消息。RocketMQ 的两大杀手锏正好对口。

💳 支付系统 RabbitMQ

支付结果要通知多个下游(订单/对账/通知),路由灵活;单笔消息量不大但延迟敏感。RabbitMQ 微秒级延迟 + 灵活路由最合适。

⚡ 秒杀系统 Kafka

秒杀瞬间 10 万+ QPS,需要极强吞吐做削峰。Kafka 百万级 TPS 扛得住,且消息可回溯便于事后分析。

💬 IM 即时通讯 RabbitMQ

消息投递要低延迟(微秒级),消息要按用户路由(路由 key 灵活),单机吞吐够用。延迟敏感型首选 RabbitMQ。

📰 内容社区 Kafka

Feed 流、点赞、评论这些行为日志海量,要做实时推荐 + 离线分析。Kafka + Flink 是标配。

📊 数据报表 Kafka

日志采集 → 数仓 → 报表,标准大数据管道。Kafka 是 ETL 的中枢。

🏢 SaaS 多租户 RabbitMQ

租户隔离可以用 vhost,路由灵活支持多租户事件分发,中小流量场景下 RabbitMQ 足够。

🚚 物流系统 RocketMQ

运单状态流转需要顺序消息(同一运单状态必须有序),延迟消息可以做"48 小时未签收自动跟进"。

🛂 风控系统 Kafka

风控要实时分析海量行为数据(点击/浏览/下单),Kafka + Flink 实时计算特征,毫秒级决策。

规律:大数据/日志/流计算 → Kafka · 电商交易/事务/延迟消息 → RocketMQ · 业务消息/复杂路由/低延迟 → RabbitMQ。中小项目不要一上来就 Kafka,运维成本不划算。

8. 选型决策树 — 4 个问题定方案

Q1:你的场景是日志采集、大数据管道、流计算吗?
是 → 直接选 Kafka。这是 Kafka 的主场,百万级吞吐 + 消息回溯 + Flink/Spark 生态无缝对接,没有第二选。
Q2:你需要事务消息吗?(本地事务 + 消息发送原子性)
是 → 选 RocketMQ。事务消息是 RocketMQ 独有强项(Kafka 有但弱、RabbitMQ 没有)。典型场景:电商下单扣库存、跨服务数据一致性。
Q3:你需要延迟消息/定时消息吗?(下单 30 分钟未支付自动取消)
是 → 优先 RocketMQ(18 个延迟级别,原生支持)。→ 次选 RabbitMQ(插件支持,但需额外配置)。Kafka 无原生支持。
Q4:你的场景需要复杂路由吗?(同一消息按规则分发到多个不同队列)
是 → 选 RabbitMQ。4 种 Exchange(Direct/Fanout/Topic/Headers)路由能力碾压,Kafka 只有 Topic 维度,RocketMQ 只有 Tag。
Q5:你的吞吐量要求有多高?
百万级 TPS → Kafka(日志、流计算)
十万级 TPS → RocketMQ(电商交易)
万级 TPS → RabbitMQ(业务消息,够用就行)
Q6:你的团队技术栈是什么?
纯 Java 团队 + 国内电商 → RocketMQ(阿里出品,文档中文,生态对路)
多语言团队 → Kafka / RabbitMQ(客户端覆盖全)
不熟悉 Erlang → 慎选 RabbitMQ(出问题排查难)
一句话决策:日志/大数据 → Kafka · 电商事务 → RocketMQ · 业务路由 → RabbitMQ不确定就选 RocketMQ(国内 Java 生态最均衡,事务/延迟/吞吐都不弱)。

9. 使用注意事项 — 8 个常见陷阱

① 消息丢失(三大环节都要防) 生产者→MQ:用 acks=all(Kafka)/发布确认(RabbitMQ)/同步发送(RocketMQ)。
MQ 自身:开启持久化 + 副本(Kafka 副本因子 3,RabbitMQ 镜像队列,RocketMQ 同步刷盘)。
MQ→消费者:关自动 ACK,处理完手动 ACK。
② 重复消费(MQ 保证"至少一次",重复必然发生) 消费者必须幂等:用 Redis 记录已处理消息 ID,或数据库唯一索引。绝对不要假设"消息只会来一次"。
③ 消息顺序(单分区有序,跨分区无序) 需要顺序的场景(订单状态变更),把同一业务 ID 路由到同一分区(Kafka 用 key,RocketMQ 用 MessageQueueSelector)。绝对不要全局顺序,性能会塌方。
④ 消息积压(消费速度跟不上生产) 临时方案:加消费者实例(注意不能超过分区数)。根本方案:优化消费逻辑(批量处理、异步化)。监控队列长度,超阈值告警。
⑤ 消费者假死(处理慢但不报错) 设置消费超时 + 死信队列。RabbitMQ 用 dead letter exchange,Kafka 用死信 topic,RocketMQ 用 DLQ。处理失败的消息进死信,人工介入。
⑥ 事务消息的回查一定要实现 RocketMQ 事务消息的 checkLocalTransaction 必须可靠实现 — 否则半消息会一直挂起,MQ 反复回查。回查逻辑要查本地数据库,不能依赖内存状态。
⑦ Topic 数量爆炸(Kafka 特别注意) Kafka 每个 Topic/Partition 都有开销(文件句柄、内存)。Topic 数量建议控制在几百以内,超过千个会显著影响性能。不要一个业务一个 Topic,用 Tag/routing key 区分。
⑧ 监控盲区(MQ 挂了不知道) 必须监控:队列长度、消费延迟、生产 TPS、消费 TPS、死信数量、broker 健康。Prometheus + Grafana 标配。出问题第一时间发现,不要等用户投诉。

10. 总结 — 三张表记住全部

速查表 1:什么时候用哪个?

场景特征首选理由
日志采集/大数据/流计算Kafka百万级吞吐 + 消息回溯 + 生态对接
电商交易/订单/分布式事务RocketMQ事务消息 + 延迟消息,Java 生态友好
业务消息/复杂路由/低延迟RabbitMQ4 种 Exchange + 微秒级延迟
秒杀/削峰(超高并发)Kafka吞吐碾压,扛得住
多服务事件分发RabbitMQTopic Exchange 路由最灵活
跨服务数据一致性RocketMQ事务消息独有
不确定选哪个RocketMQ最均衡,国内 Java 生态最稳

速查表 2:核心能力一句话

MQ一句话定位杀手锏致命短板
Kafka大数据高速公路百万级吞吐 + 消息回溯无事务消息/延迟消息
RocketMQ电商事务专家事务消息 + 延迟消息非 Java 生态弱
RabbitMQ路由小能手4 种 Exchange + 微秒延迟吞吐低 + 堆积差

速查表 3:面试一句话答

面试模板 "三者定位不同:Kafka 是大数据场景(日志/流计算,百万级吞吐,但无事务);RocketMQ 是电商场景(事务消息 + 延迟消息,Java 生态友好);RabbitMQ 是业务消息场景(路由灵活 + 微秒延迟,但吞吐低)。我做过 [项目],用的 [MQ],因为 [场景匹配]。使用上要注意 消息不丢(三环节)/幂等/顺序/积压/死信/监控 这几个点。"

常见追问 FAQ

Q1:Kafka 和 RocketMQ 都基于磁盘,为什么 Kafka 吞吐高这么多?
三个原因:① 顺序写 — Kafka 追加写日志文件,磁盘顺序写速度接近内存;② 零拷贝(sendfile) — 数据直接从页缓存到 socket,不经过用户态;③ 批量压缩 — Kafka 攒一批压缩后传输,网络开销小。RocketMQ 也用了顺序写和零拷贝,但 Kafka 的分区模型更适合高吞吐。
Q2:RocketMQ 事务消息的原理是什么?为什么能保证一致性?
两阶段 + 回查机制:① 半消息 — 生产者先发半消息到 MQ(消费者看不到);② 执行本地事务 — MQ 收到半消息后回调执行本地事务;③ 提交/回滚 — 本地事务成功提交消息(消费者可见),失败回滚消息(删除);④ 回查 — 如果本地事务超时/挂了,MQ 主动回查生产者事务状态,根据结果补提交或回滚。关键:本地事务 + 消息发送通过半消息机制实现"原子性"。
Q3:为什么说 RabbitMQ 不适合消息堆积?
RabbitMQ 消息默认在内存,堆积到阈值会刷盘,性能急剧下降。而 Kafka/RocketMQ 设计就是磁盘存储,堆积几十 GB 不影响生产(只是消费 lag)。所以 RabbitMQ 适合"消费速度跟得上"的场景,不适合"长期堆积"的场景。
Q4:如何保证消息的顺序性?
分片有序(99% 场景够用):同一业务 ID 的消息路由到同一分区 — Kafka 用 key 哈希,RocketMQ 用 MessageQueueSelector,RabbitMQ 用单队列。全局有序(极少用):整个 Topic 只用一个分区/队列,吞吐严重受限,不推荐。注意:即使分区有序,消费者也必须单线程消费该分区,否则顺序会乱。
Q5:中小项目一定要上 MQ 吗?
不一定。MQ 带来解耦/异步/削峰的同时也带来复杂度(一致性/运维/排查难度)。判断标准:① 服务间是否真的需要解耦?② 是否有削峰需求?③ 异步处理能否用线程池/定时任务替代?如果都能用更简单的方案解决,就别上 MQ。不要为了用 MQ 而用 MQ
最后一句话:消息队列是分布式系统的"血管",选型决定了系统的吞吐上限和一致性保障能力。三者不是替代关系,而是场景互补 — 大公司常常三个都用(Kafka 跑日志、RocketMQ 跑交易、RabbitMQ 跑业务消息)。理解每个的强项和短板,才能在选型时做出正确决策。
消息队列选型实战 · Kafka vs RocketMQ vs RabbitMQ · 业务系统实战 / 10