ARTICLE DETAIL

建站实战干货

来自一线的建站与推广经验沉淀,每一条都经过真实交付验证。

RocketMQ生产环境故障排查实战:从发送超时到消费堆积的完整链路

2026/9/7 14:49:22 拓冰建站 浏览量
RocketMQ生产环境故障排查实战:从发送超时到消费堆积的完整链路 1. 生产环境里RocketMQ最常出事的几个环节先说个背景。我最早接手RocketMQ是在一套日吞吐量过亿的消息集群上业务方覆盖交易、风控、通知、数据同步。刚开始那半年生产告警几乎每周都有而且有意思的是大部分问题不是RocketMQ本身崩了而是看似崩了——发送超时、消费堆积、主从切换、路由抖动每一个都够你熬夜排查。所以这篇文章我不打算讲怎么搭集群、怎么配参数那些官方文档都有。我想讲的是真正上线之后你会遇到的那些问题以及我踩过的坑、沉淀下来的排查链路。先给整套故障排查搭一个分类框架。RocketMQ生产故障本质上是四条链路出了问题发送链路、消费链路、存储链路、集群协调链路。这四类问题的现象、根因、排查手段差异很大但很多人在排查时喜欢东一下西一下最后时间花了根因没找到。我的习惯是接到告警后先给故障分类分类定了排查方向就定了。1.1 故障分类发送链路、消费链路、存储链路、集群链路发送链路的故障表现通常是客户端报错、发送超时、消息发送失败、No route info of this topic。这类问题的根因可能是网络抖动、Broker不可用、路由信息未更新、客户端配置错误。消费链路的故障表现通常是消息堆积、消费失败、重复消费、消费顺序错乱。这类问题根因可能是下游处理能力不足、消费者线程模型配置不合理、消费逻辑抛异常、Rebalance频繁触发。存储链路的故障表现通常是磁盘写满、PageCache压力过大、消息写入变慢、刷盘超时、主从同步延迟。这类问题根因可能是消息量突增、磁盘性能不足、刷盘策略配置不当、文件句柄耗尽。集群协调链路的故障表现通常是NameServer路由异常、Broker注册失败、主从切换后客户端感知延迟、脑裂问题。这类问题的根因可能是网络分区、NameServer节点故障、Broker心跳超时、客户端路由缓存未更新。有个细节很多人忽略RocketMQ的故障排查优先看日志再看指标最后才是看代码。日志和指标能把80%的问题定位到具体环节真正需要翻源码的场景非常少。1.2 我经历的典型故障场景回顾把我在生产环境遇到过的典型故障按频率排个序发送超时是出现频率最高的。尤其是大促期间业务方并发量突然上去Broker处理不过来客户端先是RT变高然后超时。这种情况下很多人第一反应是Broker是不是挂了但实际上大部分时候Broker活得好好的只是扛不住了。消费堆积排在第二。堆积的原因五花八门下游数据库连接池满了、消费者实例挂了没被踢掉、消费线程数配置不合理、消费逻辑里有个慢查询。堆积不可怕可怕的是你分不清堆积是暂时的还是持续恶化的。主从切换带来的脑裂问题虽然频率不高但杀伤力极大。一旦出现轻则消息重复消费重则消息丢失而且排查难度极高。NameServer路由问题属于看着玄乎、其实不复杂的类型。有一次生产环境某个Topic突然报No route info所有人第一反应是Topic没创建。结果查了半天才发现是某台Broker因为磁盘满被自动关闭了注册NameServer把它从路由表中移除了。这四种现象后面我会逐一展开给出完整的排查链路。2. 消息发送链路故障从超时到No route info的完整排查2.1 发送超时先分清是网络问题还是Broker处理能力问题发送超时是RocketMQ生产环境最常见的报警类型。客户端配置了sendMsgTimeout默认3000ms如果Broker在3秒内没有返回结果客户端就会抛出SendRequestTimeoutException。我处理过的发送超时案例中原因几乎都逃不出以下几类Broker线程池满载。Broker处理发送请求的核心线程池由sendMessageThreadPoolNums控制默认是CPU核数。当消息量突增线程池全部忙碌任务排队时间变长客户端就超时了。这不是Broker挂了而是Broker处理不过来了。网络链路抖动。包括客户端与Broker之间的网络延迟增大、丢包重传、防火墙限制连接数。这类问题需要结合ping、traceroute、网卡监控来判断。PageCache压力大。Broker写入消息时先写PageCache再由后台线程刷盘。如果PageCache压力大写入操作会等待内存分配导致处理变慢。这个放到存储部分细说。客户端发送端配置不合理。比如发送线程数设置过大导致Broker被压垮或者retryTimesWhenSendFailed设置过小导致瞬时故障无法恢复。排查发送超时我建议按这个顺序来第一步先看Broker的CPU、内存、磁盘IO、网络带宽。如果CPU打满说明Broker在满负荷运转需要扩容或者限流。如果磁盘IO打满重点看写入路径这个后面展开。第二步看Broker的发送线程池状态。可以通过JMX监控线程池活跃线程数、队列积压任务数。如果活跃线程数持续接近最大值队列积压任务数持续增长说明线程池满载。第三步看客户端日志中的具体报错信息。不同的报错指向不同的根因connect to ip:port failed是网络不通too many requests and system thread pool overloaded是Broker线程池满载timeout是请求超时但连接正常。这里有个我随手记录的发送超时排查小表非常实用报错关键字直接根因首选动作connect to failed网络不通或Broker宕机检查网络、Broker进程system thread pool overloadedBroker线程池繁忙查看Broker负载、扩容send request timeout请求超过3s未返回查看RT、GC、PageCacheNo route info of this topic路由信息缺失检查Topic、NameServer2.2 No route infoNameServer路由缺失的真相No route info of this topic这个报错很多刚接触RocketMQ的同学会被吓一跳以为消息丢了或者Topic不存在了。其实这个报错的含义是客户端从NameServer拉取到的路由信息中找不到对应Topic的Broker地址。我第一次遇到这个报错是在一个凌晨业务方急急忙忙找过来说消息发不出去。我当时的排查过程是这样的先确认Topic是否存在。用mqadmin topicList命令查看Topic列表Topic在。再看Topic的路由信息。用mqadmin topicRoute -t topicName查看路由结果返回空。登录Broker机器查看Broker日志发现日志里有disk space will be full的警告。执行df -h发现磁盘使用率已经到95%。Broker默认在磁盘使用率达到阈值diskMaxUsedSpaceRatio默认75%时会停止向NameServer注册路由信息。所以整个链路是这样的磁盘快满了 → Broker停止注册路由 → NameServer上的路由信息过期 → 客户端拉取不到路由 → 报No route info。这个案例告诉我们No route info不一定代表Topic有问题可能是Broker主动下线了自己。排查时如果Topic确认存在一定要去Broker端看磁盘、看注册状态。补充一个容易忽略的点客户端有路由缓存。只要客户端启动时成功拉取过一次路由路由信息就会被缓存到本地。即使后续NameServer上的路由更新了客户端在一定时间间隔内默认30s还是会使用缓存。所以当你修改了Topic的队列数或者迁移了Broker客户端可能要等一段时间才能感知到。这也是为什么有时候你在控制台改了配置客户端没立即生效。2.3 磁盘写满与发送限流Broker自我保护机制的误判上面提到磁盘写满会导致Broker停止注册路由其实磁盘问题对发送链路的影响远不止于此。RocketMQ的Broker有一套磁盘保护机制当磁盘使用率达到diskMaxUsedSpaceRatio默认75%时Broker会拒绝写入新的消息返回SYSTEM_BUSY或SERVICE_NOT_AVAILABLE。当磁盘使用率超过90%时情况会更严重可能会触发TimedOut、FlushDiskTimeout等问题。这里有个很多人误解的地方diskMaxUsedSpaceRatio的默认值是75%但这个值是相对于CommitLog目录所在磁盘的使用率不是整个系统的磁盘使用率。如果你把CommitLog和数据盘分开部署即使系统盘快满了只要数据盘使用率没到75%Broker就不会限流。磁盘写满的排查和预防我的经验是定期监控数据盘使用率告警阈值设在60%比默认的75%提前预警。设置消息过期时间fileReservedTime默认72小时可以根据业务需求调整及时清理过期消息文件。预留足够的磁盘空间。RocketMQ的CommitLog是顺序写的如果消息量突增磁盘消耗速度会非常快。大促前一定要评估磁盘空间是否充足。有个案例我记得很清楚。某次压测消息量是平时的5倍结果20分钟内数据盘从50%飙升到90%发送成功率断崖式下降。当时所有监控都没触发磁盘告警因为告警阈值设的是95%。等我们反应过来Broker已经开始限流了。所以我的建议是磁盘告警阈值宁可设低一点60%就报警别等到75%再处理那时候已经晚了。3. 消费端异常堆积、重复消费与顺序乱序3.1 消费堆积排查是下游变慢还是消费能力不足消费堆积是RocketMQ运维中的家常便饭。严格来说堆积本身不是故障而是现象。它的本质是消息生产的速度大于消费的速度。排查消费堆积我建议先回答三个问题第一堆积是持续恶化的还是暂时性的看Consumer的消费延迟CONSUME_LAG是持续增长还是趋于平稳。如果趋于平稳说明消费速度和生产速度是匹配的只是消费慢于生产如果持续增长说明消费速度在下降或生产速度在上升。第二堆积发生在哪个队列上通过mqadmin consumerProgress -g groupName查看每个队列的消费位点对比各队列的堆积情况。如果所有队列都堆积说明消费能力整体不足如果只有某个队列堆积很有可能是消息倾斜或者顺序消费卡住了。第三消费线程是在处理消息还是卡住了用jstack导出消费者进程的线程栈看消费线程的状态。如果大量消费线程阻塞在数据库操作或外部RPC调用上说明下游是瓶颈如果消费线程空闲但消息堆积说明线程数配置不合理或者Rebalance有问题。我先给你一个消费堆积排查的决策树这个思路在我处理过的绝大多数堆积问题中都是有效的消费堆积 ├── 单个队列堆积 │ ├── 顺序消费某个消息处理异常导致后面的消息全被阻塞 │ ├── 消息倾斜hash取模或队列选择策略导致数据不均匀 │ └── 消费者实例数 队列数部分消费者实例无法分配到队列 ├── 所有队列均匀堆积 │ ├── 下游处理慢数据库、外部接口、缓存成为瓶颈 │ ├── 消费线程数太少消费并发度不够 │ └── 消费逻辑本身效率低反序列化、业务逻辑耗时太长 └── 部分消费者实例堆积 ├── Rebalance问题某个实例的队列分配不稳定 └── GC问题某些实例频繁Full GC导致消费停顿3.2 重复消费的根因从At Least Once语义说起RocketMQ的消费语义是At Least Once翻译过来就是至少一次。这意味着每条消息至少会被消费一次但不保证只消费一次。所以重复消费不是Bug而是分布式系统在一致性上的必然代价。生产环境重复消费的高发场景有三个消费成功后Offset提交前发生故障。消费者处理完消息还没来得及向Broker提交消费位点进程就崩溃了。重启后Broker会按照上次提交的Offset重新推送消息这条消息就会被再消费一次。主从切换引发的重复。消费者从主Broker拉取消息并消费成功但Offset提交到了从Broker。如果这个时候发生主从切换新的主Broker上没有最新的Offset记录消费者会从旧的位点重新拉取消息。Rebalance导致的重复。消费者实例数量变化或者队列分配变化时会触发Rebalance。Rebalance过程中原本由实例A消费的消息可能被实例B接管但实例A已经拉取了一批消息在处理这些消息就会被实例B再消费一次。关于重复消费的代码层面处理我的观点是能接受重复的业务尽量接受不能接受的业务在消费端做幂等。幂等方案按复杂度从低到高排序数据库唯一索引消费消息时insert一条记录唯一键是消息的Key冲突则说明已消费。Redis SETNX消费前先SET key value NX成功才继续消费逻辑。状态机校验比如订单状态从待支付到已支付重复消费时发现状态已经是已支付直接跳过。其中唯一索引是最简单可靠的方案我推荐优先使用。Redis SETNX要考虑过期时间和分布式一致性问题状态机校验则要求业务本身有清晰的状态流转。还有一个细节消费失败重试也会造成重复。默认情况下消费失败的消息会先进入重试队列%RETRY%前缀按延迟级别依次重试。每次重试都会重新触发一次消费逻辑。如果你的消费逻辑不是幂等的重试次数越多重复消费的次数就越多。3.3 顺序消息乱序一个容易被忽略的坑RocketMQ的顺序消息分两种全局顺序和分区顺序。全局顺序要求所有消息按生产顺序消费代价是只能使用一个队列吞吐量极低。生产环境绝大多数场景用的是分区顺序即同一个Key比如同一个订单号的消息进入同一个队列消费者按顺序消费。顺序消息乱序我遇到过三种根因第一种Producer发送时Key取错。这是最隐蔽的。分区顺序消息要求Producer通过MessageQueueSelector将相同Key的消息发送到同一个队列。如果Key取错或者Selector实现有Bug相同业务实体的消息会被分散到不同队列消费时自然就乱序了。举一个我真实遇到的例子。订单业务要求同一订单的消息按顺序消费团队用订单ID作为Key。某次需求变更后代码里Key从订单ID变成了订单ID操作类型结果同一个订单的创建消息和支付消息进了不同队列消费端拿到的消息顺序号错乱。排查这种问题需要看Producer端埋点确认相同业务实体的消息被投递到了哪个队列。第二种消费者是多线程并发消费。RocketMQ的顺序消费支持MessageListenerOrderly但这个有序性是建立在一个队列同一时刻只有一个线程消费的基础上的。如果消费线程池配置了多个线程或者消费者实例数大于队列数就会导致三个队列的消息被不同线程并发处理。本质上顺序消息消费的并发度受限于队列数不要指望用多线程加速。第三种主从切换时的乱序。顺序消息的主从同步机制和普通消息不同。如果主Broker在同步完部分消息后宕机而从Broker上的消息不完整切换后消费者从从Broker拉取消息可能拉取到的位点回退导致消息重复或乱序。这个问题本质上需要集群层面保证主从数据一致性代码层面很难规避。顺序消息乱序的排查链路我的经验是先看Producer端用mqadmin queryMsgByKey -t topic -k key验证相同Key的消息是否进入了同一个队列。再看Consumer端确认消费线程模型是否与顺序消费匹配。最后查主从切换历史看乱序时间点附近是否有主从切换记录。4. 存储与PageCacheBroker内存瓶颈的深层问题4.1 PageCache压力过大导致的服务抖动RocketMQ的存储模型设计得相当精巧消息先写入PageCache操作系统的页缓存由操作系统负责将脏页刷到磁盘。这种做法在绝大多数场景下性能极佳因为顺序写PageCache的速度远高于直接写磁盘。但PageCache是一个全局共享资源它不只为RocketMQ服务还为整个操作系统上所有文件IO服务。如果机器上其他进程大量读取文件或者RocketMQ的读写比例严重失衡PageCache就会承受巨大压力。PageCache压力大的典型表现是Broker的写入RT明显变高从几毫秒飙升到几百毫秒。发送超时、消费超时同时出现。vmstat或dstat中bi块设备读入和bo块设备写出居高不下。dmesg或/var/log/messages中出现call trace、内存回收相关日志。这里要解释一个底层机制当PageCache中的脏页数量达到一定比例操作系统会启动后台回收将脏页刷到磁盘。如果脏页产生速度大于刷盘速度系统会进入直接内存回收模式这时候所有需要分配内存的进程都会被阻塞。RocketMQ的消息写入需要分配PageCache所以会被阻塞表现出来就是写入RT飙升。我处理过一次典型案例某台Broker机器上除了RocketMQ还部署了一个数据同步服务这个服务会反复读取大量小文件。结果每次数据同步任务执行时Broker的写入RT就飙到500ms以上发送超时率明显上升。最后我们把数据同步服务迁到独立机器Broker的RT恢复正常。排查PageCache压力我推荐用/usr/bin/time -v和strace这类工具但更直接的是看系统层面的指标# 查看内存回收和交换情况 vmstat 1 10 # 查看缓冲区和缓存占用 free -h # 查看脏页比例 cat /proc/vmstat | grep dirty # 查看IO等待 iostat -x 1如果确认PageCache是瓶颈解决办法有将RocketMQ部署到独立机器避免其他IO密集型进程干扰。调整刷盘策略将flushDiskType从ASYNC_FLUSH改为SYNC_FLUSH。同步刷盘虽然吞吐下降但脏页不会积压太多PageCache压力更可控。这个需要业务场景接受一定的性能下降。调整操作系统内存回收参数比如vm.dirty_background_ratio和vm.dirty_ratio。不过这个要谨慎改不好会影响整个系统的稳定性。4.2 主从同步延迟与数据一致性RocketMQ支持主从架构主节点负责写入从节点负责同步和读取。同步方式分两种同步复制SYNC_MASTER和异步复制ASYNC_MASTER。同步复制Producer发送消息时Broker会等消息同步到从节点后才返回成功。这种方式数据一致性高但RT会明显增加。异步复制Broker写入本地后立即返回后台线程再异步同步到从节点。这种方式RT低但主从切换时可能丢失少量消息。主从同步延迟的排查核心看两个指标同步延迟。通过mqadmin brokerStatus -b brokerAddr查看putMessageEntireTimeMax、getMessageTransferedTime等指标。从节点的落后位点。正常情况从节点应该紧跟主节点如果从节点位点落后很多说明同步链路有问题。主从切换时的数据一致性是生产环境的重点。RocketMQ的主从切换不是自动的需要借助HA方案比如NameServer配合brokerId0配置或者RocketMQ 5.x的自动故障转移。但不管用什么方案切换瞬间的消费位点回退问题都值得警惕主节点异步复制模式下部分消息可能只存在于主节点从节点还没有同步。主节点宕机后从节点晋升为主节点但它缺少最后几条消息。消费者从新主节点拉取消息时会发现位点比旧主节点靠前消息被跳过。更严重的是如果消费者恰好从旧的位点拉取会发现消息缺失而一直阻塞。所以主从切换后的消息对账很有必要。我建议的检查方法是切换完成后用mqadmin consumerProgress -g groupName查看每个队列的消费位点再用mqadmin queryMsgByOffset验证位点上的消息是否存在。如果发现消费位点回退或者消息缺失要立即启动消息补偿流程。4.3 文件句柄耗尽的排查实例文件句柄耗尽这个问题比PrePageCache更隐蔽但后果一样严重。RocketMQ的存储引擎会打开大量文件CommitLog文件、ConsumeQueue文件、IndexFile、以及各种锁文件。消息量大时文件句柄数量轻松超过几万个。默认情况下Linux系统对单个进程的文件句柄数限制是1024这个值在现代系统中已经很少见很多系统默认是65535。如果你没有修改过ulimit -nRocketMQ在文件句柄数达到上限后会报Too many open files错误。我遇到过的一个真实案例某个集群的Broker每隔几天就会挂一次每次挂之前日志里都有大量Too many open files报错。当时我们的排查路径是先看/proc/{pid}/fd统计进程打开的句柄数确认是否达到上限。再通过lsof -p {pid} | awk {print $NF} | sort | uniq -c | sort -rn | head -20看哪些文件被大量打开。定位到是/store/commitlog/下的文件句柄占用了绝大多数。最终发现是因为这个集群的Topic非常多每个Topic的数量大于合理值。RocketMQ为每个Topic的每个队列维护了ConsumeQueue文件Topic过多导致文件数激增。解决方法是限制Topic数量和队列数量同时将ulimit -n调整到655350RocketMQ官方推荐值。这里有一个容易踩的坑修改ulimit -n要同时修改/etc/security/limits.conf中的nofile而且要重新登录或者重启Broker进程才会生效。别问我怎么知道的我曾经在线上改了配置文件没重启以为生效了结果下一个高峰期Broker又被文件句柄打挂了一次。5. NameServer与集群拓扑的隐蔽问题5.1 NameServer无状态设计的优势与陷阱NameServer是RocketMQ的注册中心负责管理Broker的路由信息。它被设计成无状态节点节点之间不互相通信也不做数据同步。每个NameServer节点独立维护一份全量的路由信息客户端和Broker向任意一个NameServer发送请求拿到的是同一个视图在数据一致的情况下。这个设计的优势很明显部署简单、水平扩展容易、故障恢复快。但陷阱同样明显第一NameServer宕机后运行中的集群不受影响但新启动的客户端无法获取路由。因为客户端启动时需要从NameServer拉取路由信息。如果所有NameServer都不可用新启动的客户端就无从获取路由消息发送全部失败。第二NameServer之间的数据不一致是可能的。虽然Broker会向所有NameServer注册但如果注册过程中某个NameServer短暂不可用它就会缺失部分路由。当客户端正好连上了这个NameServer就会拿到不完整的路由信息。针对NameServer的问题我的建议是至少部署两个NameServer节点最好机架隔离。客户端配置多个NameServer地址用分号分隔不要只配一个。定期巡检NameServer的堆内存。NameServer虽然无状态但路由信息都在内存里Topic多的时候内存占用也不小。监控NameServer的注册接口延迟。Broker每30s向NameServer发送一次心跳如果心跳处理延迟Broker会被判定为失效。5.2 集群中某台Broker路由异常的定位集群中某台Broker的路由异常通常表现为部分客户端发送消息失败但其他客户端正常或者某个Topic的消息时好时坏。我遇到过一次比较典型的案例。集群有4个Broker节点2主2从。某天突然收到告警说某个核心Topic的发送成功率只有60%。排查过程先看客户端日志发现报错的客户端连的都是broker-a这个地址。登录broker-a查看Broker日志没有发现错误。用mqadmin clusterList查看集群信息发现broker-a挂在集群里但broker-a的从节点状态异常。进一步检查发现broker-a的主节点和从节点之间的复制链路断了导致broker-a主节点上的消息无法同步到从节点。在同步复制模式下主节点会等待从节点确认从节点跟不上主节点的写入就会变慢甚至超时。这个案例暴露了一个容易被忽略的事实主从复制链路的状态直接影响发送链路的健康度。排查Broker路由问题时不能只看主节点状态还要看从节点的同步状态。定位Broker路由异常的完整排查顺序我建议是这样1. 用 mqadmin clusterList -n nameServerAddr 确认集群拓扑看所有Broker是否都在线。 2. 用 mqadmin brokerStatus -b brokerAddr 检查每个Broker的关键指标写入RT、主从延迟、线程池状态。 3. 用 mqadmin topicRoute -t topicName 检查Topic的路由信息看Topic在每个Broker上是否都有完整的队列。 4. 用 mqadmin consumerProgress -g groupName 检查消费者在这台Broker上的拉取情况。 5. 登录Broker机器看日志重点看REMOTING线程池、存储模块、复制模块的输出。这套流程走下来绝大多数路由异常都能定位到具体环节。如果还不行再考虑用jstack抓取Broker进程的线程栈看是否有线程卡死。6. 故障排查工具箱命令、日志与排查节奏6.1 常用运维命令的实战用法RocketMQ自带的mqadmin命令是排查故障的第一利器但很多同学对它不够熟悉。我在这里整理几个生产环境最常用的命令和用法。查看集群状态# 查看集群中有哪些Broker mqadmin clusterList -n 192.168.1.10:9876 # 查看某个Broker的状态 mqadmin brokerStatus -b 192.168.1.11:10911brokerStatus输出中我最关注的是putMessageEntireTimeMax消息写入的最大耗时、getMessageTransferedTime消息拉取耗时、以及线程池相关的指标。如果putMessageEntireTimeMax超过100ms说明Broker写入路径存在瓶颈。查看Topic路由# 查看某个Topic的路由信息 mqadmin topicRoute -t orderTopic -n 192.168.1.10:9876 # 查看所有Topic列表 mqadmin topicList -n 192.168.1.10:9876topicRoute的输出包括Topic分布在哪些Broker上、每个Broker上有几个队列。如果某个Broker上的队列数为0说明路由异常。查看消费进度# 查看消费者组的消费进度 mqadmin consumerProgress -g orderConsumerGroup -n 192.168.1.10:9876 # 查看某个Topic的消息堆积情况 mqadmin consumerProgress -g orderConsumerGroup -t orderTopic -n 192.168.1.10:9876consumerProgress输出中DIFF列就是消费延迟。通过对比不同时间点的DIFF可以判断堆积是在增长还是收敛。查看消息内容# 根据Key查询消息 mqadmin queryMsgByKey -t orderTopic -k orderId123 -n 192.168.1.10:9876 # 根据Offset查询消息 mqadmin queryMsgByOffset -t orderTopic -b 192.168.1.11:10911 -i 123456注意queryMsgByKey需要IndexFile索引如果消息量太大导致索引文件被覆盖Key查询会失败。这种情况下只能通过queryMsgByOffset指定位点查询。6.2 关键日志的定位方法RocketMQ的日志分布在不同模块快速定位到正确的日志文件是排查效率的关键。Broker日志store.log存储模块日志。刷盘失败、磁盘满、文件异常都会记录在这里。commitlog.logCommitLog相关操作。写入失败、文件加载失败重点关注。rebalance.log消费者的Rebalance日志。如果消费者频繁触发Rebalance这里会有大量记录。rocketmq_client.log客户端日志在应用进程的日志目录下。NameServer日志namesrv.logNameServer运行日志。Broker注册、路由变更都会记录在这里。namesrv-warn.logNameServer的警告日志特别是Broker心跳超时的警告。排查时我通常会在日志中搜索以下关键字关键字含义No route info路由信息缺失disk space will be full磁盘空间告警system thread pool overloaded线程池满载flush disk timeout刷盘超时commitlog file not foundCommitLog文件缺失offset not found位点不存在Too many open files文件句柄耗尽一个实用技巧生产环境日志量很大直接用grep搜关键字很容易漏掉上下文。我习惯配合grep -C 20前后各20行来带上下文输出这样能快速看到异常前的完整调用链。6.3 一次完整故障排查的时间线最后我用一次真实的故障排查经历来串一遍完整的方法论。这是一次典型的发送超时消费堆积双告警事件。10:00收到告警核心Topic发送成功率下降至85%消费者组有消费堆积。10:05登录监控平台查看Broker的CPU、内存、磁盘IO指标。发现Broker CPU在60%左右磁盘使用率正常但网络入带宽达到80%。10:10测试客户端到Broker的连通性延迟正常。排除网络链路问题。10:15查看Broker的store.log发现大量flush disk timeout日志。结合网络入带宽80%的情况初步判断是大量消息涌入导致磁盘IO压力增大。10:20用iostat -x 1查看磁盘IO发现%util接近100%await超过50ms。确认磁盘是瓶颈。10:30检查消息量监控发现某条业务线在10:00上线了一个批处理任务导致消息量突增3倍。磁盘IO承受不住Broker写入变慢发送超时消费也因为没有新消息而堆积消费堆积是因为消息积压导致消费位点落后而不是消费变慢。10:40联系业务方暂停批处理任务允许消息积压等待磁盘IO回落到正常水平。10:50待消息量回落后确认发送恢复消费堆积逐渐消化。11:30积压消息全部消费完发送成功率恢复。事后复盘决定为这条业务线单独划分Topic限制消息量波动对核心链路的冲击。这次排查的教训是故障发生时先看硬件指标CPU、磁盘、网络再看软件日志最后根据业务上下文做判断。这个顺序虽然简单但能避免你想当然地把问题归因于某个环节。RocketMQ的故障排查本质上是对消息流转全链路的理解。从Producer到NameServer从Broker到Consumer每一环都可能在特定条件下出问题。多经历几次线上故障把排查链路练成肌肉记忆你就能在告警响起时迅速定位到问题所在了。最后分享一个小经验每次重大故障解决后我都会把排查过程整理成一份文档包括现象、时间线、根因、临时方案、长期改进措施。这个习惯帮我在后续多次故障中快速定位因为很多故障的根因高度相似一次复盘能省下下次一半的排查时间。如果你刚开始接触RocketMQ生产运维可以从今天遇到的问题开始整理积累到几十个case之后你对这个系统的理解会上一个大台阶。