跳到正文

RocketMQ 监控指标与告警 ​

一句话:RocketMQ 的告警要顺着 CommitLog 的写路径往下看——PageCache 和磁盘决定写延迟,线程池决定会不会拒绝,分发和消费进度决定业务什么时候真正拿到消息。

适用版本:RocketMQ 4.9 / 5.3 / 5.5。mqadmin 子命令和运行时字段在不同版本略有差异,指标名与默认值以对应版本官方文档和实际输出为准;阈值只是起点,按自己的基线校准。

相关:03-消息存储与持久化、04-高可用与主从复制、07-死信队列与消费重试、08-生产故障场景处置。

为什么需要 ​

RocketMQ 的故障很少以「Broker 挂了」出现。更常见的是:PageCache 刷不动,发送 RT 先升高;线程池排满后开始拒绝,生产端超时重试;或者消息已经写进 CommitLog,但分发落后,消费者看到的是延迟。等业务报「消息收不到」,现场往往已经错过了磁盘和线程池这两个先兆。

监控要回答四个问题:还写得进去吗、写得快不快、消费者还追得上吗、主从还同步吗。四个问题的指标和处置动作不同。

核心机制 ​

四个观察面 ​

观察面主要指标/字段反映什么恶化后的先兆
写入发送 TPS、putLatency99/putLatency999、pageCacheLockTimeMills写入耗时和 PageCache 竞争发送 RT 升高、超时
分发dispatchBehindBytesCommitLog 到 ConsumeQueue 的异步分发进度消息已落盘但消费端看不到
线程池sendThreadPoolQueueSize、putThreadPoolQueueSize、pullThreadPoolQueueSize 及对应 *Capacity请求排队深度队列贴满、拒绝、重试
磁盘commitLogDiskRatio、commitLogDirCapacity、df -hCommitLog 所在盘余量75/85/90% 三档水位
消费consumerProgress 的 diff、消费 TPS、%RETRY%、%DLQ%消费是否跟上堆积、重试、死信
主从slaveFallBehind、SYNC_MASTER 的 syncFlushTimeout=5000主从同步是否落后切换丢消息、发送失败
路由NameServer 9876 可用性、clusterList客户端能否拿到路由新客户端无法发送
资源JVM GC、fd、网络进程是否停顿心跳、同步、消费线程超时

字段名以 getBrokerRuntimeInfo 和 brokerStatus 的实际输出为准;不同 4.x/5.x 小版本的键可能增减,采集端要容忍缺字段。

链一:PageCache/磁盘慢 → 写延迟 → 线程池拒绝 → 重试放大 ​

RocketMQ 的 CommitLog 是顺序追加,写入先落 PageCache,再由刷盘线程落盘。PageCache 脏页回落跟不上时,写线程会被阻塞,pageCacheLockTimeMills 升高,putLatency99 跟着升高。

延迟传导到 sendThreadPoolQueueSize / putThreadPoolQueueSize:队列贴满后新请求被拒绝,生产端收到超时或拒绝,开始重试;重试继续打同一个 Broker,PageCache 更忙,形成正反馈。ASYNC_FLUSH 下宕机还会丢最后约 500ms 的写入;SYNC_FLUSH 延迟更高,但 RPO 更小。

链二:分发落后 → 消费延迟 ​

消息写入 CommitLog 后,还要由分发线程异步构建 ConsumeQueue。dispatchBehindBytes 持续增长,说明消费端还看不到最新消息——即使 Broker 的写入 TPS 正常,业务侧也会感到延迟。磁盘慢、CommitLog 过大、单条消息过大都会拖慢分发。

分发落后和消费堆积要分开看:前者是 Broker 内部没处理完,后者是消费者没追上。看 consumerProgress 的 diff 时,先确认 dispatchBehindBytes 没有同步增长;否则扩消费者没有意义。

链三:消费堆积 → 重试 → 死信 → 业务补偿 ​

消费失败会进入 %RETRY%<consumerGroup>,并发消费默认重试 16 次、累计约 4 小时 46 分后进入 %DLQ%<consumerGroup>。积压时重试消息和正常消息争抢队列,消费 TPS 进一步下降;DLQ 增长后,业务只能靠人工或补偿任务处理。

consumerProgress 的 diff 持续上涨、消费 TPS 正常,通常是容量不足;消费 TPS 很低,先查下游 RT、数据库锁和 Full GC;TPS 为 0 但实例在线,查消费线程是否被卡住或 Rebalance 未完成。

链四:主从落后 → 切换丢消息 ​

ASYNC_MASTER 下 Master 宕机,Slave 可能缺少最后一段写入;SYNC_MASTER 下 Master 要等 Slave 同步,Slave 不可用时发送会等满 syncFlushTimeout=5000 后失败,而不是静默降级成异步。slaveFallBehind 持续增长,说明复制已经落后,此时做切换要按 RPO 评估,不能假设 Slave 一定完整。

DLedger/Controller 模式用多数派选主,RTO 更短,但同样要监控同步落后和选主状态;经典主从默认不会自动切换,Master 挂了需要人工或外部编排。

链五:NameServer 不可用 ​

客户端先向 NameServer 拿路由,再直连 Broker。NameServer 全挂后,已有客户端还能用本地缓存的路由继续发送,但新客户端拿不到路由,Topic 变更也不会生效。NameServer 之间不复制数据,靠每个 Broker 向所有 NameServer 注册;监控要逐个实例探活,不能只看一个地址。

最小上手 ​

bash
sh bin/mqadmin clusterList -n 127.0.0.1:9876
sh bin/mqadmin brokerStatus -b 127.0.0.1:10911 -c DefaultCluster
sh bin/mqadmin getBrokerRuntimeInfo -b 127.0.0.1:10911
sh bin/mqadmin consumerProgress -n 127.0.0.1:9876 -g order-consumer
sh bin/mqadmin topicStatus -n 127.0.0.1:9876 -t order-topic
sh bin/mqadmin topicStatus -n 127.0.0.1:9876 -t '%DLQ%order-consumer'

brokerStatus 在部分 5.x 版本直接返回运行时信息,getBrokerRuntimeInfo 在部分版本不存在;以 mqadmin help 和实际输出为准。rocketmq-dashboard(原 console)可以替代部分查询。

Prometheus 用 rocketmq-exporter,默认监听 5557:

bash
java -jar rocketmq-exporter.jar \
  --rocketmq.config.namesrvAddr=127.0.0.1:9876
curl -s localhost:5557/metrics | grep -E \
  'rocketmq_(broker_tps|broker_qps|producer_offset|consumer_offset|message_accumulation)'

积压增长速率可以直接从 offset 算(官方 exporter README 的示例写法,阈值按业务调整):

promql
(sum(irate(rocketmq_producer_offset[1m])) by (topic))
- on(topic) group_right
(sum(irate(rocketmq_consumer_offset[1m])) by (group, topic)) > 100

生产动作 ​

以下阈值均为起点,按基线校准;告警要带 Broker、Topic、消费组三个维度。

告警条件级别先做什么
CommitLog 磁盘commitLogDiskRatio 大于 0.75 预警、0.85 严重、0.90 拒写P1/P0清理过期文件、扩盘或迁 Topic;不删 commitlog
写延迟putLatency99 超过基线 2 倍或持续大于 200msP1查磁盘 IO、PageCache、单条大消息
PageCache 竞争pageCacheLockTimeMills 持续高于基线P1查刷盘压力和 vm.dirty_*,不要先重启
分发落后dispatchBehindBytes 持续增长或高于基线P1查磁盘和 CommitLog 大小;消费扩容无效
线程池排队send/put/pullThreadPoolQueueSize 大于容量 80% 持续 1m;拒绝计数增长或日志出现拒绝P1限流生产或扩 Broker;加线程可能加重磁盘
消费堆积consumerProgress 的 diff 超过阈值且 10m 斜率向上P1/P2扩消费者到 Queue 数,再查下游
重试/死信%RETRY% 或 %DLQ% 的 maxOffset 持续增长P1修消费逻辑,按 DLQ 重投流程处理
主从落后slaveFallBehind 持续增长;SYNC_MASTER 发送 RT 接近 5sP1查复制网络和 Slave 磁盘;不要盲目切换
NameServer/Broker任一 NameServer 9876 不可用;clusterList 缺 BrokerP0恢复实例;已有客户端可能仍靠缓存路由
GCFull GC 停顿大于 1s 或老年代大于 80%P1调堆、查泄漏;GC 会拖慢分发和消费

先扩容/限流还是先重启:

  • 磁盘到 85% 以上:先清理过期文件、扩盘或迁 Topic;90% 已经拒写,重启不会释放空间,手工删 CommitLog 还会破坏索引。
  • PageCache 忙、写延迟高:查磁盘 IO 和刷盘配置;重启会让 PageCache 变冷,恢复期更慢。
  • 线程池贴满:先限流生产或扩 Broker;单纯加线程会把压力推到磁盘,可能更糟。
  • 消费堆积:先把消费者扩到 Queue 数(不能超过),再优化下游;重启消费者会触发 Rebalance,积压更重。
  • 主从落后:RPO=0 时不能切到落后的 Slave;先修复制,或让 SYNC_MASTER 继续失败而不是静默降级。

处置顺序:磁盘 → PageCache/写延迟 → 线程池 → 消费堆积 → DLQ。先保住写入路径,再处理业务补偿;不要在磁盘满时重启 Broker 期待自愈。

边界 ​

RocketMQ 的优势是业务消息语义丰富:延迟/定时消息、事务消息、顺序消息、Tag/SQL 过滤、重试与 DLQ、LiteTopic;CommitLog 统一顺序写 + ConsumeQueue 轻量索引,适合万级 Topic 和业务事件。rocketmq-dashboard 和 mqadmin 让日常排障比较直接。

代价是 CommitLog 和 PageCache 强绑定:写入快依赖内存和顺序写,磁盘一慢就传导到线程池;分发是异步的,写入成功不等于可消费;主从/Controller、DLQ、重试都需要专门运维;并行度受 Queue 数约束,生态规模不如 Kafka。

扬长避短:用 RocketMQ 承载订单、支付、通知这类需要事务、延迟、顺序和过滤的业务消息;Queue 数按目标消费者数的 2 倍规划;核心链路用 SYNC_MASTER 或 DLedger/Controller 守住 RPO;监控以 Broker 写路径和消费组 diff 为主。超大吞吐的日志/流式管道交给 Kafka;低延迟点对点调用不要用消息队列硬扛。

讲清楚 ​

RocketMQ 的告警顺着写路径看。CommitLog 先写 PageCache,脏页回落跟不上时 pageCacheLockTimeMills 和 putLatency99 先升,接着 sendThreadPoolQueueSize 贴满、请求被拒,生产端重试又把压力打回来。写入成功不等于可消费,dispatchBehindBytes 持续增长说明分发落后,这时扩消费者没用。消费侧看 consumerProgress 的 diff,堆积会进重试 Topic,约 4 小时 46 分后进 DLQ,业务补偿压力随之而来。主从看 slaveFallBehind,异步复制下切换可能丢最后一段,同步复制下 Slave 不可用会直接发送失败。磁盘 75/85/90% 三档要提前处理,重启和手工删 CommitLog 都不解决问题。RocketMQ 强在事务、延迟、顺序和过滤,代价是 CommitLog/PageCache 运维和主从复杂度;超大流式吞吐交给 Kafka。

把一门技术讲透到既懂也会用