基于本文回答

播面 播面

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

生产环境中,如果某个 Topic 的消费组(Consumer Group)在线但无法消费,应该按照什么步骤进行排查?

在生产环境中,Consumer Group “在线但无法消费”(即应用进程存活,在 Kafka 侧显示连接正常,但 Lag 持续增加,消息未被处理)是一个非常经典的故障场景。

由于现代 Kafka 客户端(如 Java 客户端自 KIP-62 引入后)将 心跳线程(Heartbeat Thread)拉取/处理线程(Poll/Processing Thread) 分离,最常见的原因是处理线程被阻塞,而心跳线程仍在正常发送心跳,导致 Kafka 认为消费者存活,从而不触发 Rebalance。

以下是标准的生产环境排查步骤,按 “现象确认 -> 客户端排查 -> 服务端排查” 的逻辑进行:


第一步:确认现象与范围(缩小排查范围)

  1. 确认是否有新消息产生
    • 检查 Topic 的生产速率(Produce Rate),确认是真的“没有消费”,还是“生产者根本没发消息”。
  2. 检查消费积压(Lag)状态
    • 执行命令:
      bash
      kafka-consumer-groups.sh --bootstrap-server <broker_ip:9092> --describe --group <your_group_id>
    • 看分配(ASSIGNMENT): 消费者是否被分配到了 Partition?如果没有分配到,说明处于 Rebalance 异常状态。
    • 看积压(LAG): 积压是在所有 Partition 上增加,还是只在某几个特定的 Partition 上增加?(如果只有个别 Partition 堵塞,通常是遇到了“毒药消息”或特定数据的处理死锁)。
    • 看状态(STATE): 确认 Group 的状态是 Stable(稳定)、PreparingRebalance(准备重平衡)还是 CompletingRebalance。如果是频繁的 Rebalance,消费也会停滞。

第二步:深入排查消费者应用端(最常见故障区)

如果 Group 状态是 Stable 且分配了 Partition,但就是不消费,问题 90% 出在消费端应用

  1. 检查应用日志(Error & Warning)
    • 是否有 CommitFailedException?(处理时间过长,超过了 max.poll.interval.ms)。
    • 是否有反序列化异常(Deserialization Exception)?
    • 是否有 OutOfMemoryError 或频繁的 Full GC 日志?
  2. 抓取线程快照(Thread Dump) - 核心操作
    • 如果是 Java 应用,立即执行 jstack <pid> > jstack.log
    • 寻找 Kafka Poll 线程和业务处理线程: 检查这些线程的状态是否为 BLOCKEDWAITING
    • 常见死锁/阻塞原因:
      • 业务代码在请求外部接口(如 HTTP、RPC、数据库操作),且没有设置超时时间,导致线程永久挂起。
      • 获取数据库连接池(Connection Pool)被耗尽,线程在等待连接。
      • 死锁(Deadlock)。
  3. 检查消费者限流或暂停逻辑
    • 业务代码中是否触发了 consumer.pause() 并且因为某种 bug 遗漏了 consumer.resume()
  4. 检查参数配置
    • fetch.min.bytes / fetch.max.wait.ms:是否配置过大,导致消费者一直在傻等足够多的数据?
    • max.poll.records:一次拉取太多,导致处理超时。

第三步:排查消息本身问题(毒药消息 / 大消息)

如果只有一个或部分 Partition 停止消费:

  1. 毒药消息(Poison Pill)
    • 某条消息的格式损坏,导致业务代码抛出未捕获的异常,或者反序列化失败。消费者无限重试这条消息,导致 Offset 无法提交。
    • 对策: 查看日志是否有循环报错。如果有,需要在代码中捕获异常并将错误消息发送到死信队列(DLQ),然后手动 ack(或提交 offset)。
  2. 超大消息导致卡死
    • Topic 中出现了大于消费者 max.partition.fetch.bytes(默认 1MB)的消息。消费者不断尝试拉取,但因为消息太大拉不下来,陷入死循环。

第四步:排查 Kafka Broker 服务端与网络问题

如果客户端完全没有异常日志,线程也没有阻塞:

  1. 检查 Partition 的 Leader 状态
    • 检查 Topic 状态:kafka-topics.sh --describe --topic <topic_name>
    • 确认是否存在 Offline Partitions,或者 Leader 所在的 Broker 宕机。如果没有 Leader,消费者是无法拉取数据的。
  2. 网络连通性(非对称网络问题)
    • 消费者连接 Kafka 集群时,是先连接某一个 Broker 获取 Metadata,然后再去连接具体的 Partition Leader 所在的 Broker 消费。
    • 坑点: 消费者可能能连通 Metadata Broker(所以在线),但网络 ACL 或防火墙限制了它连接真正的 Leader Broker。
    • 对策: 在消费端机器上 telnetnc 测试所有 Broker 节点的 9092 端口。
  3. 服务端限流(Quotas)
    • 检查 Kafka 集群是否对该 Client ID 或 User 配置了严格的读取限流(Fetch Quota),导致读取速度被限制到接近于 0。
  4. ACL 权限问题
    • 确认是否有运维人员误操作收回了该 Consumer Group 的 Read 权限。

总结:快速应急处理套路(SOS)

在生产故障期间,为了尽快恢复业务,可以采取以下应急动作:

  1. 保留现场: 重启前,务必先打线程 Dump (jstack) 和内存 Dump (jmap),并保存当前应用的完整日志。
  2. 尝试重启: 重启一个 Consumer 实例。如果重启后瞬间恢复消费,但过一段时间再次卡死,100% 是业务代码存在阻塞(如连接池耗尽、外部 API 超时未设置)。
  3. 跳过毒药消息: 如果怀疑是特定消息导致崩溃,可以使用 kafka-consumer-groups.sh 工具强制修改 Offset,跳过卡住的消息(慎用,会丢数据):
    bash
    kafka-consumer-groups.sh --bootstrap-server <broker> --group <group> --topic <topic> --reset-offsets --shift-by 1 --execute
  4. 扩容/隔离: 临时增加新的 Consumer 实例,看是否能接管其他正常的 Partition 缓解部分业务压力。
00:00
00:00