1. 这次宕机的最初半小时:现象、告警与现场
下午两点多,我正在处理一个需求,监控群里突然开始刷消息。先是Kafka生产端延迟告警,接着消费端lag持续拉高,最后直接收到broker节点失联的告警。我打开监控面板,发现集群里三个节点的分区副本开始大量下线,Controller已经发生了两次切换。整个过程从第一条告警到集群不可写,前后不到半小时,连给人反应的时间都不够。
复盘的时候看时间线,大致是这样:
- 14:05:某Topic的分区leader开始频繁切换,生产端出现
NotLeaderForPartitionException - 14:12:broker-2的磁盘使用率超过85%,触发了阈值告警
- 14:19:消费者组lag从几百条飙到几十万条,消费速率几乎归零
- 14:23:第一个broker被判定失联,Controller发起新一轮leader选举
- 14:27:部分分区进入
UnderReplicatedPartitions状态,生产端大量报错,集群基本瘫痪
这种故障最磨人的不是宕机本身,而是你并不知道它下一步会往哪个方向发展。
客户端日志里当时出现了这样的报错:
code复制org.apache.kafka.common.errors.NotLeaderForPartitionException:
This server is not the leader for that topic-partition
以及一些连接超时、LeaderNotAvailableException、TimeoutException。生产端的数据写不进去,消费端也卡住不动。从应用侧的视角看,Kafka集群像是一台突然熄火的老旧发动机,所有齿轮全部咬死。
我先把现场情况稳住——暂停了部分非核心消费任务的提交,避免消费进度被自动提交机制写坏。然后就开始逐层往下排查。下面把这个排查链路完整写出来,其中有很多命令、判断逻辑是我反复用过的,希望能给遇到类似情况的朋友一些参照。
需要模型API调用? 免费领10W Token,多模型网关一键接入 Claude、DeepSeek 等主流模型。
2. 从客户端报错一路挖到集群节点:我的完整排查链路
2.1 第一层判断:客户端报错,核心是元数据过期
很多人一看到NotLeaderForPartitionException就以为分区leader挂了,其实要分两种情况。一种是leader确实切换中,另一种是客户端持有的元数据长期没刷新,指向了旧leader。
那次现象里,生产端在短时间内集中报出这类错误,同时伴随大量连接重置。我先在客户端测了一下broker连通性:
bash复制kafka-broker-api-versions.sh --bootstrap-server broker-1:9092
这条命令能快速拿到broker支持的协议版本、broker当前是否可响应。如果某个broker直接卡死,命令会超时,这比看监控面板更直观。当时broker-2的响应时间明显异常,但在TCP层面还能连上,说明进程还活着,但JVM内部出了问题,或者底层资源已经阻塞。
接下来看broker端的server.log,出现大量这种日志:
code复制INFO [ReplicaFetcherManager on node 2] Truncated partition ...
WARN [ReplicaFetcherThread nodeId=2] Error for partition xxx-0
org.apache.kafka.common.errors.NotLeaderForPartitionException
这说明broker之间副本同步也出了问题。一个节点既不能正常响应客户端,又无法拉取其他节点的副本数据,整个集群的元数据状态开始变得混乱。
注意一个容易被忽略的坑:如果客户端配置了advertised.listeners和实际监听地址不一致,也会出现客户端报错、看起来像集群不可用的情况。那次我检查了server.properties,没有发现配置错误,于是把重点转移到broker节点本身。
2.2 第二层判断:分区状态异常,优先看副本分布和ISR
我登录一台客户端机器,用AdminClient脚本查看具体的分区状态:
bash复制kafka-topics.sh --bootstrap-server broker-1:9092 --describe --topic order-events
输出里能看到每个分区的leader、replicas和isr列表。当时不少分区ISR里只剩一个副本,其他副本都已经从ISR中掉出。更麻烦的是,ISR中的那个副本在broker-2上,而broker-2正是被判定失联的那台。
这里有个背景知识:Kafka的可用性其实取决于ISR(In-Sync Replicas)。只要leader所在broker还活着,且ISR数量大于min.insync.replicas,生产端还能继续写入
