说实话,我们团队在消息队列上踩过的坑,足以写一本"血泪史"了。
最初我们用的是 RabbitMQ,单机跑得好好的,QPS 上了 5000 就开始报警。后来换成 Kafka,以为能一劳永逸,结果运维复杂度直接起飞。等我们终于把 Kafka 调稳了,业务方又开始吐槽延迟太高,RocketMQ 又被提上了议程。
这套流程走下来,我算是把三款主流消息队列的特点摸透了。今天不聊理论,直接上生产数据,告诉你什么场景该选什么。
选型前,先把场景搞清楚
我见过太多团队是"跟风选型"——别人用 Kafka 我也用 Kafka,别人换 RocketMQ 我也换。这种做法,十有八九会踩坑。
高吞吐量大数据场景,选 Kafka 没错,日均千亿级消息量 Kafka 就是为这个设计的。
业务事务消息场景,选 RocketMQ,它原生支持事务消息和延迟消息,开箱即用。
中小型系统,快速迭代场景,RabbitMQ 反而是最优解,学习成本低,客户端库丰富,社区活跃。
听起来简单对吧?但现实往往是你接手的系统三个条件都沾点边。
生产环境数据对比
我把这些年踩过的坑总结成一张表,全部是实打实的生产数据:
| 维度 | Kafka | RabbitMQ | RocketMQ |
|---|---|---|---|
| 单机 QPS(纯队列) | 10w+ | 3-5w | 5-8w |
| 端到端延迟(P99) | 5-20ms | 1-3ms | 2-5ms |
| 事务消息支持 | 需自研 | 插件支持 | 原生支持 |
| 延迟/定时消息 | 支持但不完善 | 插件支持 | 原生支持,精度ms |
| 运维复杂度 | 高 | 中 | 中 |
| 消费模式 | 分区消费,顺序可控 | 经典队列,负载均衡 | 广播/集群消费 |
第一个坑:Kafka 顺序问题
我们有个订单系统,上 Kafka 之后发现消息乱序。业务逻辑是:创建订单 → 支付 → 发货。消息乱序的结果是,用户还没支付就收到发货通知了。
排查了三天,发现问题出在 Partition 分配策略。Kafka 的分区消费是按 Partition 维度的,如果你的消费者数量小于 Partition 数量,部分 Partition 会被闲置;但如果消费者数量大于 Partition 数量,多出来的消费者就会空跑。
解决方案:
# 查看 Topic 的 Partition 和 Consumer Group 关系bin/kafka-consumer-groups.sh --bootstrap-server localhost:9092\--describe--grouporder-consumer-group# 输出示例:# TOPIC PARTITION CURRENT-OFFSET LOG-END-OFFSET LAG CONSUMER-ID# order-events 0 1520 1580 60 consumer-1# order-events 1 1400 1450 50 consumer-2# order-events 2 - 1500 - consumer-3注意consumer-3的 LAG 是-,说明这个消费者根本没分配到 Partition。解决办法是确保消费者数量不超过 Partition 数量,或者把 Partition 数调大。
第二个坑:RabbitMQ 内存爆炸
RabbitMQ 某个队列的消息突然堆积,内存占用从 2GB 飙到 12GB。原因是消费者挂了,但消息没被 ACK,RabbitMQ 以为消费者还在处理,持续堆积。
排查步骤:
# 查看队列消息数和内存占用rabbitmqctl list_queues name messages memory durable\--formattercsv>/tmp/queue_status.csv# 查看消费者的 ACK 状态rabbitmqctl list_consumers queue_name consumer_tag\ack_details prefetch_count发现问题消费者是 Python 写的,某个异常没捕获,进程崩了但没抛出让 RabbitMQ 感知到。消息在队列里躺了 6 个小时才被发现。
避坑建议:
- 一定要设置队列最大长度:
x-max-length或x-max-length-bytes - 一定要设置消息 TTL:
x-message-ttl,防止积压太久变质 - 消费者必须 try-catch,并在 finally 里做幂等处理
# RabbitMQ 队列声明示例arguments:x-max-length:100000# 队列最大消息数x-message-ttl:86400000# 消息24小时过期x-dead-letter-exchange:dlx.exchange# 死信队列第三个坑:RocketMQ 消费超时
切到 RocketMQ 之后,发现有时候消费者处理慢了,整个消费进度卡住。原因是 RocketMQ 的长轮询机制和 Kafka 不一样——Kafka 是消费者主动拉取,RocketMQ 是 Broker 推送给消费者,但中间有个"排队"的概念。
Consumer 端:
// 正确配置:提高消费并发度Propertiesprops=newProperties();props.put("consumerThreadMin",20);// 最小消费线程props.put("consumerThreadMax",60);// 最大消费线程,建议 CPU 核数的 2-3 倍props.put("pullInterval",100);// 拉取间隔msprops.put("pullBatchSize",32);// 批量拉取消息数还有一个坑是 RocketMQ 的消费顺序。Kafka 可以通过 Partition 保证单 Partition 内有序,RocketMQ 的顺序消息需要单独配置:
// 顺序消费示例consumer.subscribe("order-topic","*");consumer.registerMessageListener((MessageListenerOrderly)(msgs,context)->{for(MessageExtmsg:msgs){// 处理订单消息processOrder(newString(msg.getBody()));}returnConsumeOrderlyStatus.SUCCESS;});注意用MessageListenerOrderly而不是MessageListenerConcurrently,前者会加锁保证同一队列消息串行消费。
选型决策树
说了这么多,给你们一个实操决策树:
你的场景是? ├── 日均消息量 > 1000万,吞吐优先 │ └── 选 Kafka,重要前提:你能接受较高运维复杂度 │ ├── 需要事务消息(订单、支付场景) │ └── 选 RocketMQ,别犹豫,Kafka 自研事务成本太高 │ ├── 需要延迟/定时消息(订单超时关闭、任务调度) │ └── RocketMQ 延迟消息精度可达 ms 级别,Kafka 要靠外部调度 │ ├── 团队小(< 5人),快速交付优先 │ └── RabbitMQ,上手快,文档丰富,出问题好排查 │ └── 混合场景(又想高吞吐又想事务) └── RocketMQ + Kafka 分层 高吞吐业务日志用 Kafka 核心交易链路用 RocketMQ写在最后
消息队列选型没有银弹。不要被"XX 是最牛的"这种言论带偏了,脱离场景谈技术选型就是在耍流氓。
我们最终的架构是:核心交易链路用 RocketMQ 保障事务和延迟消息,海量日志采集用 Kafka 保障吞吐,日常任务通知用 RabbitMQ 保障快速接入。
记住一句话:合适的才是最好的,能解决你问题的是那个运维成本和收益平衡点上的选择。
附:常见错误说法纠正
- ❌ “Kafka 是最好的消息队列,什么场景都用它” → 延迟和运维复杂度了解一下
- ❌ “RabbitMQ 性能不行,早该淘汰了” → 中小场景下,RabbitMQ 的易用性秒杀其他两个
- ❌ “RocketMQ 只有阿里在用,不成熟” → 阿里双十一扛过无数次验证,稳定性不用怀疑