3. Broker故障诊断与恢复

Broker 是 Kafka 集群的数据承载核心,一旦单个 Broker 出现异常,轻则导致分区 Leader 切换、客户端重试,重则引发 ISR 收缩、副本不同步甚至数据丢失。Broker 故障往往不是单一原因,而是“进程崩溃、GC 停顿、请求超时、线程池耗尽、日志段损坏”这五类问题相互交织。本节按“先定位、再隔离、后恢复”的思路,逐一拆解这五类故障的识别信号与应急动作。

一、进程崩溃:先看退出码与 OOM 记录

Broker 进程消失是最直观的故障。定位第一步是确认进程是否真的退出,而不是被监控误判:

# 查看 broker 进程与最近退出状态
ps -ef | grep kafka.Kafka | grep -v grep
systemctl status kafka   # 若使用 systemd 托管
# 查看内核 OOM Killer 是否杀死了 JVM
dmesg -T | grep -i "killed process" | grep java
# 查看 broker 自身日志尾部
tail -n 200 /var/log/kafka/server.log

若 dmesg 中出现 “Out of memory: Killed process … java”,说明是操作系统 OOM Killer 杀死了 JVM,根因通常是堆外内存(页缓存、DirectByteBuffer、网络缓冲)或堆内存设置过大挤占了系统内存。应急处理:先通过副本机制恢复该 Broker 承载的分区可用性,再调整 KAFKA_HEAP_OPTS,一般建议堆内存不超过物理内存的 50%,并预留页缓存空间。若日志显示 java.lang.OutOfMemoryError: Java heap space,则需检查是否有异常大的请求或分区数量激增。

二、GC 停顿:用 GC 日志量化停顿时间

GC 停顿不会让进程退出,但会让 Broker 在数秒内无法响应请求,表现为客户端超时、Controller 误判 Broker 下线。定位关键是开启并分析 GC 日志:

# 启动参数中开启 GC 日志(JDK 11 示例)
-Xlog:gc*:file=/var/log/kafka/gc.log:time,uptime:filecount=10,filesize=50M
# 快速统计停顿分布
grep "Pause" /var/log/kafka/gc.log | awk -F'[ ,]+' '{print $NF}' | sort -n | tail -20

判断标准:单次 Full GC 停顿超过 1 秒,或 Young GC 频繁到每秒多次,就应视为异常。常见根因是堆内存过小、对象分配速率过高(大量小分区、频繁创建请求对象)。应急处理:临时扩容堆内存并重启,长期则考虑升级 JDK、切换 G1/ZGC、减少分区数量或优化客户端批量参数。注意,重启会触发 Leader 迁移,应避开业务高峰。

三、请求超时与线程池耗尽:从 JMX 指标切入

请求超时和线程池耗尽常常同时出现。Kafka 的网络线程(num.network.threads)负责收发,请求处理线程(num.io.threads)负责读写日志。当 IO 线程被慢磁盘或大请求占满,请求队列堆积,客户端就会超时。定位应查看以下 JMX 指标:

指标含义异常阈值参考
RequestQueueSize等待处理的请求数持续 > 100 需警惕
ResponseQueueSize待发送响应数持续增长说明网络或客户端慢
RequestHandlerAvgIdlePercentIO 线程空闲率< 0.3 表示线程接近耗尽
NetworkProcessorAvgIdlePercent网络线程空闲率< 0.3 表示网络层繁忙

应急处理:若空闲率极低且磁盘 util 接近 100%,说明瓶颈在磁盘,应检查是否有分区正在做副本追赶或日志刷盘过频;可临时调大 num.io.threads 并重启,或限流大客户端。若队列堆积但磁盘空闲,则可能是某个客户端发送了超大消息,可通过 message.max.bytes 与客户端日志定位。

四、日志段损坏:识别、隔离与恢复

日志段损坏通常由磁盘故障、断电或文件系统异常引起。Broker 启动时会校验日志段,若发现 .log 或 .index 文件损坏,会在日志中报出 CorruptRecordException 或 KafkaStorageException。定位命令:

# 检查日志目录下的分区与段文件
ls -lh /data/kafka/logs/topic-a-0/
# 使用自带工具校验索引与日志一致性
kafka-log-dirs.sh --bootstrap-server broker1:9092 --describe \
  --topic-list topic-a
# 查看 broker 日志中的损坏记录
grep -i "corrupt\|KafkaStorageException" /var/log/kafka/server.log

应急处理分两种情况:若该分区还有其他可用副本,最安全的做法是删除损坏 Broker 上的该分区目录,让副本重新同步;若所有副本都损坏,则只能使用 kafka-dump-log 尝试恢复可读数据,或从备份恢复。删除目录前务必确认其他副本处于 ISR 且数据完整,否则会造成数据丢失。

Broker 五类故障定位与应急处理路径 进程崩溃 进程消失 / OOM GC 停顿 响应卡顿 / 超时 请求超时 客户端重试 线程池耗尽 队列堆积 日志段损坏 文件校验失败 定位信号 dmesg OOM 退出码 / 堆栈 定位信号 GC 日志 Pause > 1s 即异常 定位信号 RequestQueueSize 空闲率 < 0.3 定位信号 IO 线程空闲率 磁盘 util 100% 定位信号 CorruptRecord KafkaStorageException 应急处理决策 隔离故障 Broker → 副本接管 → 修复重启 内存 / GC 类 调堆 ≤ 物理内存 50% 切换 G1 / ZGC 后重启 线程 / 请求类 调大 num.io.threads 限流大客户端 / 查慢盘 日志损坏类 删损坏目录让副本重同步 全副本损坏则 dump 恢复

五、恢复后的验证与预防

无论哪类故障,恢复后都必须验证三件事:一是该 Broker 上的分区是否重新加入 ISR,可用 kafka-topics.sh --describe 查看;二是 Controller 是否稳定,避免频繁 Leader 选举;三是客户端是否仍有超时。预防层面,建议为 Broker 配置独立的 GC 日志与 JMX 导出,设置磁盘水位告警,并定期用 kafka-log-dirs.sh 巡检日志目录。对于日志段损坏,最有效的预防是使用带校验的文件系统、避免非正常断电,并保留副本数 ≥ 2。

应急处理的核心原则:先保证集群可用(让副本接管),再修复故障节点。不要在业务高峰直接重启 Broker,除非该节点已完全不可用。