Skip to content

MQ 选型与堆积排查

RabbitMQ vs Kafka vs RocketMQ 全景对比、线上百万级消息积压应急处置三板斧、Erlang 节点内存报警排障。

Updated View as Markdown
For humans

MQ 选型与堆积排查

校招考察频度:☆ 中频(大厂实战大题·架构选型与线上避坑) 生产导向主线:“周一早高峰上游突发流量激增,RabbitMQ 积压了 500 万条消息,消费者处理极慢,内存即将触发报警,线上怎么紧急抢救?”这是大厂二面和架构面最爱考察的线上实战题。面试核心:主流三大 MQ(RabbitMQ / Kafka / RocketMQ)全景选型权衡,以及线上百万级消息积压的标准化“应急三板斧”。


一、主流消息队列技术选型横向对比

在架构设计与技术选型时,必须根据业务核心诉求进行取舍,没有最好的中间件,只有最适合场景的架构:

选型维度RabbitMQApache KafkaApache RocketMQ
开发语言Erlang(高并发电信级基础)Scala / JavaJava(阿里开源,深度贴合 Java 生态)
单机吞吐量万级 ~ 十万级 TPS百万级 TPS(依赖 PageCache 零拷贝)十万级 ~ 三十万级 TPS
端到端延迟微秒级(μs 级),极致低延迟毫秒级(ms 级,强制攒批)毫秒级(ms 级)
路由灵活性极高(Direct / Fanout / Topic / Headers)单纯依靠 Topic 分区,无复杂路由支持 Tag 标签过滤与 SQL92 属性过滤
延迟/死信队列原生死信(DLX) + 官方延迟插件无原生支持(需业务自建或上时间轮)原生支持 18 个固定时间等级的延时消息
顺序消息支持单队列单消费者 / 一致性哈希分流单个 Partition 内部天然绝对保序顺序消息原生支持良好(按 Sharding Key)
分布式事务本地消息表模式事务消息机制(偏重于流计算内部)原生两阶段半消息(Half Message)事务
高可用方案现代 Quorum 队列(Raft 共识)KRaft 集群模式(彻底消除 ZK 依赖)Dledger 多副本集群(Raft 协议)
工业界适用场景企业级核心业务、订单交易、异步通知、Python (Celery) / Java 后端首选大数据采集、海量日志上报(ELK)、实时流式计算(Flink/Spark)金融级电商大促交易、超大规模高并发业务、复杂事务消息

二、生产事故:线上百万级消息积压应急排查三板斧

大厂技术面经典灵魂拷问:“监控报警突然响起,RabbitMQ 某个核心队列积压了 500 万条订单消息,消费者处理速度几乎归零,马上就要打爆 Broker 磁盘与内存,你怎么在半小时内解决?”

1. 紧急止血与根因排查(1~5 分钟)

  • 查日志与监控:首先登录监控控制台,确认积压原因:
    • 是否消费者大面积崩溃或发布了 Bug 代码导致频繁报错?(若是,立即回滚新版本代码!)
    • 是否下游关系型数据库发生死锁、慢 SQL 或连接池耗尽卡死了工作线程?
    • 是否调用的外部第三方支付网关发生网络超时?
  • 快速恢复消费者:如果是消费者连接打满或慢查,紧急调大消费者线程池,或临时屏蔽耗时的非关键外部调用(服务降级)。

2. 方案一:常规水平扩容(消费速度 < 积压速度时的标准解法)

若消费者本身没有 Bug,只是因为上游突发活动流量暴涨(比如平时每秒 500 条,活动突发每秒 10,000 条):

  • 检查消费者的 CPU 和内存占用,如果单机资源尚有富余,直接调大 prefetch_count(如从 50 调至 200)并增加消费并发线程数(concurrency)。
  • 如果单机算力已满,立即在 Kubernetes 集群中对消费者 Deployment 执行横向弹性扩容(HPA),将 Pod 副本数从 5 个瞬间扩容到 30 个并发抢单消费。

3. 方案二:紧急创建“临时分流队列”(积压量极大时的终极核武器!)

如果受限于下游业务或数据库性能瓶颈,直接增加消费者会把 MySQL 瞬间打死,必须采用临时分发跳板法:

[原积压队列: 500 万条]
       |
       |-- 1. 紧急上线【临时分发消费者】(只从原队列把消息拉出来,不做任何业务入库!)
       v
[临时分流 Exchange (Fanout / Hash)]
       |
  +----+----+----+----+ ... (并行分发到 30 个临时子队列)
  v    v    v    v    v
 [Q1] [Q2] [Q3] [Q4] [Q30]
  |    |    |    |    |
  v    v    v    v    v
 [30 倍真正的业务消费者群,通宵并行消费落库!]
  • 第一步:临时编写一个极端轻量的“搬砖程序”(转发应用),部署几十个实例。这个程序不做任何业务计算、不查数据库、不调外部接口,唯一的任务就是以几万 QPS 的极限速度将原积压队列中的消息捞出来,快速投递到一个新建的临时交换机中。
  • 第二步:临时交换机下面绑定 20 ~ 30 个临时队列。
  • 第三步:紧急部署 20 ~ 30 组真实的业务消费进程,分别监听这 20 ~ 30 个临时队列。
  • 收益:原本 1 个队列的并发瓶颈被瞬间拆解成了 30 个独立通道,消费吞吐瞬间提升 20 倍以上,在极短时间内将几百万积压安全消化!

4. 方案三:积压消息超期导致的死信排查

  • 如果积压时间持续了几个小时,部分配置了 TTL 的重要消息可能已经过期被转入了死信队列(DLX)。
  • 在主队列消费完毕后,必须编写专用的离线清洗脚本,从死信队列中将由于超时被踢出的订单捞出来,进行对账与离线重放补偿,防止产生业务漏单。

三、RabbitMQ 生产运维避坑:内存与磁盘告警流控

RabbitMQ 拥有极严格的自我保护机制(Alarm & Flow Control):

  1. vm_memory_high_watermark 内存告警:
    • 默认内存阈值是物理内存的 0.4(40%)。
    • 一旦内存占用超过 40%,RabbitMQ 会立即向所有生产者连接发送 connection.blocked 帧,主动挂起并阻断所有生产者的写入 Socket!应用层发消息会陷入超时假死。
  2. disk_free_limit 磁盘剩余空间告警:
    • 默认剩余可用磁盘低于 50MB 时,Broker 同样会彻底阻断写入。
  3. 生产排查与恢复命令:
    # 查看集群状态与告警
    rabbitmqctl status
    # 查看各队列积压数量与内存消耗
    rabbitmqctl list_queues name messages_ready messages_unacknowledged memory

面试高频追问

  1. 为什么你们的项目选 RabbitMQ 而不是吞吐更高的 Kafka?
    • 绝大部分后端在线业务(电商交易、审批、通知)的实际流量在几百到几千 QPS,RabbitMQ 单机数万 QPS 的能力完全绰绰有余。
    • RabbitMQ 具备微秒级低延迟、极其强大的 Exchange 路由规则、灵活的延迟消息插件、死信队列与单条精确手动 ACK。而 Kafka 虽然吞吐极高,但消息缺乏单条确认、路由单一、无原生灵活延时队列,且强制攒批带来毫秒级延迟,更适合大数据日志场景。
  2. 消费者挂了很久,RabbitMQ 里的消息会丢失吗?
    • 只要声明了持久化队列(durable=true)且消息发送时指定了持久化(delivery_mode=2),消息就会安全保存在磁盘上,绝对不会丢失。等消费者排障恢复重新上线后,Broker 会立即继续向其推送处理。
Navigation

Type to search…

↑↓ navigate↵ selectEsc close