基于本文回答

播面 播面

文图音视,全方位拆解八股文
0
评论

凌晨3点线上报警提示Kafka某个Topic积压了上百万条消息,请你讲讲从排查瓶颈到紧急扩容清理积压的完整SOP

知识点图片

凌晨3点处理线上严重报警,核心原则是:“先止血恢复业务,后排查根因;防雪崩重于一切”

Kafka积压上百万条消息,意味着数据实时性已经遭到严重破坏。以下是一份标准、冷酷且高效的凌晨3点应急处理SOP(标准作业程序)


阶段一:初步定级与止血评估(0-10分钟)

目标:确认影响范围,决定是否需要叫醒其他同事,评估是否有引发连环雪崩的风险。

  1. 确认报警真实性与积压趋势:
    • 查看监控看板(Grafana/Prometheus):当前Lag(积压量)是100万,生产速率(Produce Rate)和消费速率(Consume Rate)分别是多少?
    • 趋势判断:是逐渐累积到100万的(慢积压),还是突然激增的(突刺型)?
  2. 评估业务影响:
    • 该Topic对应什么业务?(核心链路如交易/支付,还是非核心如日志/埋点?)
    • 核心链路:立即拉群,叫醒业务研发Owner和DBA。
    • 非核心链路:记录状态,尝试单人操作恢复。
  3. 💥 核心警告:防下游雪崩
    • 在未确认下游系统(如MySQL/Redis/ES/第三方API)抗压能力前,绝不可盲目扩容消费者! 否则清理积压产生的并发洪峰会把下游直接打挂,导致更大面积的P0故障。

阶段二:快速排查瓶颈(10-25分钟)

目标:定位是生产者突发、消费者故障,还是Kafka集群问题。

通过APM工具(如SkyWalking, Pinpoint)、日志平台或Kafka命令排查以下三种可能:

1. 消费者端问题(占90%以上)

  • 实例状态:去K8s/容器平台看消费者的Pod是不是在频繁重启(CrashLoopBackOff)?是否有OOM(内存溢出)?
  • 消费线程卡死:消费者状态是Running,但消费速率为0。
    • 排查手段:查看业务日志,看是否有调用第三方接口超时、死锁、或者慢SQL。使用 Arthas / Jstack 快速打印线程堆栈,看 KafkaMessageListenerContainer 线程阻塞在哪。
  • 频繁Rebalance(重平衡)
    • 现象:消费速率时有时无。
    • 原因:消费者处理消息太慢,超过了 max.poll.interval.ms(默认5分钟),被踢出消费组,导致消费组不断重平衡,所有消费暂停。
  • 毒药消息(Poison Pill):某条坏数据导致反序列化失败或业务抛出未捕获异常,一直在无限重试(Retry Storm),阻塞了当前Partition的消费。

2. Kafka集群问题(占5%)

  • 查看Kafka集群Broker的CPU、IO、网络负载。
  • 查看是否有节点宕机导致Partition Leader切换失败。
  • 数据倾斜(Hotspot):100万积压是不是全堵在某1个Partition里?如果是,说明生产者的路由Key设计有问题。

3. 生产者端问题(占5%)

  • 现象:消费速率正常甚至偏高,但生产速率暴增了10倍。
  • 原因:上游在跑大批量刷数据脚本、或者遭遇了DDoS攻击、或者是某个重试Bug导致发了海量废消息。

阶段三:紧急扩容与清理积压实操(25-60分钟)

目标:根据排查结果,采取对应的清理手段。

场景A:正常消费跟不上生产(需安全扩容)

前提:下游数据库/接口确认能抗住数倍并发。

  1. 查看当前的分区数与消费者数量关系
    • Kafka金科玉律:一个Partition只能被同一个消费组里的一个消费者实例消费。
  2. 扩容策略
    • 情况1:当前消费者实例数 < Partition数
      • 操作:直接在K8s/容器平台增加消费者实例(Pod)数,直至 实例数 = Partition数
    • 情况2:当前消费者实例数 = Partition数
      • 操作:此时单纯加Pod无效(会闲置)。必须先扩容Topic的Partition数量。
      • 命令kafka-topics.sh --alter --zookeeper x.x.x.x:2181 --topic my-topic --partitions <新数量>
      • 再扩容:增加消费者Pod数跟上新Partition数。
      • (注意:如果业务对消息顺序有严格要求,由于分区增加会改变Key的哈希路由规则,强顺序性业务不可随意扩分区,需走场景B)

场景B:下游抗不住 / 紧急疏散降级(旁路处理)

如果扩容会把数据库打死,或者消费逻辑极其复杂导致慢。

  1. 写一个极简的“搬运工”消费者
    • 连夜编写/部署一个极简服务,不查DB,不调API,只做一件事:从当前积压的Topic拉取消息,直接原封不动地写到一个临时的Topic(如 topic-temp-dump)或者写入HDFS/OSS/Redis等高速存储。
  2. 切流
    • 让这个极简服务满载运行,快速把这100万条消息搬走。业务Topic恢复空闲,实时新数据得以正常处理。
  3. 后续补录
    • 白天上班后,再安排任务从临时存储/Topic里慢慢回放、补偿这100万条旧数据。

场景C:毒药消息/无限重试卡死(跳过积压)

如果发现是某几条坏数据导致报错卡死,或者这100万条数据是上游发错的废数据(例如日志),可以直接抛弃。

  1. 修改偏移量(Reset Offsets)直接跳到最新
    • 停止当前消费者服务。
    • 使用命令将消费组的偏移量重置到最新(跳过这100万条):
      kafka-consumer-groups.sh --bootstrap-server broker1:9092 --group my-group --topic my-topic --reset-offsets --to-latest --execute
    • 重启消费者服务,直接消费新产生的实时数据。

场景D:消费者处理太慢导致的频繁Rebalance

不需要扩容,只需临时改配。

  1. 临时调大 max.poll.interval.ms(例如从 300000 改为 900000)。
  2. 临时调小 max.poll.records(例如从 500 改为 50),让单次拉取的数据量变少,保证在规定时间内处理完,避免被踢出消费组。
  3. 重启消费者。

阶段四:观察与复盘(修复后及次日工作)

  1. 凌晨持续观察(30分钟)
    • 盯紧Grafana面板:Lag线是否呈明确下降趋势(如断崖式下降或稳步下滑)。
    • 盯紧下游组件:数据库连接池、CPU、IO是否报警。
    • 确认最新产生的消息是否能在一分钟内被处理。
  2. 次日白天的Post-Mortem(故障复盘)
    • 代码优化:将单条插入数据库改为批量插入(Batch Insert);将同步调用改为异步(CompletableFuture)。
    • 架构优化:如果经常有突发洪峰,引入真正的消息死信队列(DLQ)机制。
    • 监控优化:100万积压才报警可能太晚了。报警规则不应仅看“积压数量”,而应增加“消息延迟时间(Lag Time)”报警。例如:积压数可能才1万,但消费速率极慢,第一条消息已经延迟了10分钟,这就应该报警了。

💡 凌晨3点操作纪律:

  • 敲击任何 alterreset-offsets 命令行之前,粘贴到记事本复查一遍,确认Topic名称和集群地址(千万别切错生产和测试环境)。
  • 操作前在群里同步:“正在执行消费组偏移量重置,预计影响 xxx,耗时 x 分钟”,留下操作轨迹。
00:00
00:00