Kafka 2.11-0.10.1.0 故障分析与处理
问题描述
线上环境每过一段时间(一个月左右) kafka就会故障无法访问,三个服务器都正常,kafka进程正常,但是会出现一个节点和其他两个节点断开的情况,表现形式上类似脑裂,日志中显示正常节点无法连接到故障的节点
出现问题 就不会再自动恢复了
重启故障节点即可解决这个问题
环境信息
集群
三台机器组成的kafka集群
- 192.168.1.217 RD-MQ-01
- 192.168.1.218 RD-MQ-02
- 192.168.1.219 RD-MQ-03
kafka 版本
kafka_2.11-0.10.1.0
zookeeper 版本
3.4.10-4181
ZK独立部署,不与其他程序共用
java 版本
1 | [root@RD-MQ-01 ~]# java -version |
操作系统版本
CentOS 6.8
1 | [root@RD-MQ-02 bin]# uname -a |
相关配置
kafka
1 | ############################# Server Basics ############################# |
错误日志
开发环境下出现了该问题时相关日志信息
217上的kafka日志
1 | [2020-04-22 10:43:36,369] WARN [ReplicaFetcherThread-1-219], Error in fetch kafka.server.ReplicaFetcherThread$FetchRequest@5641fe72 (kafka.server.ReplicaFetcherThread) |
218上的kafka日志
1 | [2020-04-22 10:59:42,687] WARN [ReplicaFetcherThread-1-219], Error in fetch kafka.server.ReplicaFetcherThread$FetchRequest@1722c608 (kafka.server.ReplicaFetcherThread) |
219上的kafka日志
1 | [root@RD-MQ-03 logs]# tail -f server.log |
ZK 日志
zk日志正常,zk数据文件正常(大小正常,有定期清理)
问题排查记录
检查网络
实际上这三个机器都是互通的
ping
1 | # 217 |
telnet
telnet 也是通的
1 | [root@RD-MQ-02 ~]# telnet 192.168.1.219 9092 |
netstat
1 | [root@RD-MQ-02 ~]# netstat -an | grep 219:9092 |
连接一直在建立,然后关闭
检查Zookeeper
ZK 能正常操作读写数据
在ZK中能看到3个kafka的brokers
1 | [zk: 192.168.1.218:5181(CONNECTED) 4] ls /brokers/ids |
检查kafka manager
只能查询到 Brokers 信息
无法查看topic 和 Consumers
使用kafka命令查看
topic list
分别在三个机器上查看topic信息,都能列出
1 | ./kafka-topics.sh --list --zookeeper 192.168.1.217:5181 |
topic describe
挑选一个测试队列【st.topic.yery.test】 查看详情:
1 | [root@RD-MQ-01 bin]# ./kafka-topics.sh --zookeeper 192.168.1.217:5181 --topic st.topic.yery.test --describe |
consumer-groups list
查看消费者
1 | [root@RD-MQ-02 bin]# ./kafka-consumer-groups.sh --bootstrap-server 192.168.1.217:9092 --list |
consumer-groups describe
僵死,无法列出消费者的相关信息
1 | [root@RD-MQ-02 bin]# ./kafka-consumer-groups.sh --bootstrap-server 192.168.1.217:9092 --group dp.device.consumer --describe |
换一个消费者组
能看到两个分区的消费信息,实际上应该是32个分区
1 | [root@RD-MQ-01 bin]# ./kafka-consumer-groups.sh --bootstrap-server 192.168.1.217:9092,192.168.1.218:9092,192.168.1.219:9092 --group by.consumer.online.storage --describe |
使用Kafka命令行工具测试
用命令行生产者 和 消费者 进行测试
消费者
无法消费到数据任何,没有错误
1 | ./kafka-console-consumer.sh --bootstrap-server 192.168.1.217:9092,192.168.1.218:9092,192.168.1.219:9092 --topic by.topic.install.car.device --from-beginning |
生产者
发送数据报错
1 | [root@RD-MQ-02 bin]# ./kafka-console-producer.sh --broker-list 192.168.1.217:9092,192.168.1.218:9092 --topic by.topic.install.car.device |
查进程信息
进程占用的文件句柄数
217
1 | [root@RD-MQ-01 opt]# lsof -p 13517 | wc -l |
218
1 | [root@RD-MQ-02 ~]# lsof -p 13557 | wc -l |
219
1 | [root@RD-MQ-03 ~]# lsof -p 81068 | wc -l |
可以看到219占用了非常多的文件句柄数
网络端口9092查看
217
存在很多 time_wait 状态的连接
1 | tcp 0 0 ::ffff:192.168.1.217:9092 ::ffff:192.168.1.215:47810 ESTABLISHED 13517/java |
1 | [root@RD-MQ-01 ~]# netstat -antp | grep 9092 | wc -l |
218
基本正常
1 | tcp 0 0 ::ffff:192.168.1.218:9092 ::ffff:192.168.1.216:48369 ESTABLISHED 13557/java |
1 | [root@RD-MQ-02 ~]# netstat -antp | grep 9092 | wc -l |
219
存在大量 CLOSE_WAIT 状态的连接
1 | tcp 247 0 ::ffff:192.168.1.219:9092 ::ffff:192.168.1.217:58837 CLOSE_WAIT 81068/java |
数量远多于其他两台
1 | [root@RD-MQ-03 opt]# netstat -antp | grep 9092 | wc -l |
查线程堆栈内存信息
使用内存
基本正常
217
1 | ps aux|grep java|grep "kafka"|awk '{print $6}' |
218
1 | ps aux|grep java|grep "kafka"|awk '{print $6}' |
219
1 | ps aux|grep java|grep "kafka"|awk '{print $6}' |
jstack
在219上找到死锁
1 | Found one Java-level deadlock: |
jmap
dump 219 上的堆内存信息
1 | jmap -dump:format=b,file=kafka-219-jmap.dump 81068 |
先保留现场,必要时再进行分析,文件大小:665M
相关资料
匹配度高的
KAFKA-3994
https://issues.apache.org/jira/browse/KAFKA-3994
executor-Heartbeat线程 与 kafka-request-handler线程 互锁
死锁信息与我们发现的有一点差异,没有group-metadata-manager线程
版本比我们用低一个版本
影响版本:0.10.0.0
修复版本:0.10.1.1, 0.10.2.0
KAFKA-4399
https://issues.apache.org/jira/browse/KAFKA-4399
死锁信息与我们发现的有一个差异,没有executor-Heartbeat线程
影响版本:0.10.1.0
修复版本:0.10.1.1,0.10.2.0
KAFKA-4478
https://issues.apache.org/jira/browse/KAFKA-4478
心跳执行器、组元数据管理器、请求处理器 相互锁死了
这个的死锁堆栈信息与我们碰到的一致
影响版本:0.10.1.0
修复版本:无 (在bug 3994中被解决了)
KAFKA-4562
https://issues.apache.org/jira/browse/KAFKA-4562
死锁情况与我们的一样
影响版本:0.10.1.0
修复版本:无 (在bug 3994中被解决了)
相关性高的
邮件列表一
https://users.kafka.apache.narkive.com/7ekMKZSX/deadlock-using-latest-0-10-1-kafka-release
3个线程的死锁堆栈信息,和我们的一样,文件句柄耗尽
作者已经很肯定 KAFKA-3994中的补丁可以很好地解决该问题。
关联:KAFKA-3994
邮件列表二
https://users.kafka.apache.narkive.com/mP9QlK2j/connectivity-problem-with-controller-breaks-cluster
从描述的问题来看,我们的一致
关联:KAFKA-4477
KAFKA-4477
https://issues.apache.org/jira/browse/KAFKA-4477
我们的故障节点 ISR 也减少了,也有文件句柄数和死锁相关的描述
影响版本:0.10.1.0
修复版本:0.10.1.1
结论
Kafka程序内部死锁导致了集群的故障问题
根据官方文档这个问题在issues [KAFKA-4399] 中被解决了
可以通过将现有的kafka升级至 kafka 0.10.1.1 来解决该问题
kafka 0.10.1.1 发版说明
https://archive.apache.org/dist/kafka/0.10.1.1/RELEASE_NOTES.html.
官方发版说明中已经记录解决了bug [KAFKA-4399]
- [KAFKA-3994] - Deadlock between consumer heartbeat expiration and offset commit.
- [KAFKA-4399] - Deadlock between cleanupGroupMetadata and offset commit
升级
kafka服务端
服务器端升级一个小版本到 0.10.1.1
https://archive.apache.org/dist/kafka/0.10.1.1/RELEASE_NOTES.html.
这个版本主要是修复bug,没有什么新的改变,这样我们的数据应该是完全兼容的,只需要更换服务端的包,复制配置文件,之后逐个重启kafka服务即可。
官方升级说明文档
http://kafka.apache.org/0101/documentation.html#upgrade
程序的客户端
全部的 生产者 和 消费者 都升级一个小版本到 0.10.1.1
1 | <!-- https://mvnrepository.com/artifact/org.apache.kafka/kafka-clients --> |
这样相关的代码应该都无需修改,Api不会有什么改动,只需要升级依赖包即可
修改父项目的pom文件依赖,子项目梳理一下,重新打包部署即可
其他问题
可能存在一些项目使用的是更老版本的API 和 连接方式等,如不是直连kafka而是老的连接zk的,这部分的项目需要修改代码
升级步骤记录
下载文件
1 | wget https://archive.apache.org/dist/kafka/0.10.1.1/kafka_2.11-0.10.1.1.tgz |
这里选择的scala 版本是 2.11,之前装的也是2.11的
解压到新目录
使用原来的配置位置
注意:数据文件(log)的位置
准备生产者和消费者
先准备一个生产者,产生数据
1 | ./kafka-console-producer.sh --broker-list 192.168.1.217:9092,192.168.1.218:9092,192.168.1.219:9092 --topic st.topic.yery.test |
在准备一个消费者,消费数据
1 | ./kafka-console-consumer.sh --bootstrap-server 192.168.1.217:9092,192.168.1.218:9092,192.168.1.219:9092 --topic st.topic.yery.test --from-beginning |
升级过程保持不变
停止节点219
先关闭节点219,观察集群状态:
217 218 报错了,但是集群整体运行正常
测试验证
生产者能正常产生数据
消费者能正常消费数据
消费者会打印一个警告
进入kafka manager 可以查看到只有两个 Broker(217、218)
查看topic st.topic.yery.test 的信息
所有Partition 的leader都分到217 和 218 上
In Sync Replicas 只有(217,218)
修改配置文件
复制原配置文件到kafka_2.11-0.10.1.1
1 | cd kafka_2.11-0.10.1.0 |
在219启动新版本的kafka
直接启动219 (0.10.1.1版本的)
查看219日志,可以看到在恢复数据
数据恢复完成之后 会加入集群
加入集群后,217 和 218 会打印日志,将分区ISR从[217,218] 扩大到 [217,218,219]
在kafka manager 上可以看到Broker 219 加入了集群
查看topic [st.topic.yery.test] 的信息
Partition 的leader分到217,218 和 219 三台上了
In Sync Replicas 有(217,218,219)
再次验证测试
此时命令行 生产数据 消费数据都是正常的
应用程序测试
直接验证binlog程序
程序在不升级kafka客户端包版本的情况下测试
在数据库中修改 by_device 表的记录x,将记录x的remark修改为‘kafka update’
可以在缓存管理中看到记录x的remark改成了‘kafka update’
对应的队列 qa.topic.binlog.base-by_device
Sum of partition offsets 也加了1,表明消息发送消费正常
程序在不升级kafka-clients 包的情况下,也没有问题
再升级217 和 218
用同样的步骤更换 217 和 218 上的kafka程序,配置文件就用原来的对应的配置文件
逐个关闭kafka程序再启动,等待数据库恢复,集群恢复到正常状态
验证老数据
升级完成之后再从头开始消费队列[st.topic.yery.test]的数据
1 | ./kafka-console-consumer.sh --bootstrap-server 192.168.1.217:9092,192.168.1.218:9092,192.168.1.219:9092 --topic st.topic.yery.test --from-beginning |
数据正常,升级完成
补充
升级说明
从0.8.x,0.9.x,0.10.0.x,0.10.1.x或0.10.2.x升级到0.11.0.0
http://kafka.apache.org/0102/documentation.html#upgrade
Kafka 0.11.0.0引入了新的消息格式版本以及有线协议更改。通过遵循以下建议的滚动升级计划,可以保证升级期间不会停机。但是,请在升级之前查看0.11.0.0中的显着更改。
从版本0.10.2开始,Java客户端(生产者和消费者)已经具有与较早的代理进行通信的能力。版本0.11.0的客户可以与版本0.10.0或更高版本的代理通信。但是,如果您的代理早于0.10.0,则必须先升级Kafka群集中的所有代理,然后再升级客户端。版本0.11.0代理支持0.8.x和更高版本的客户端。
###################################
升级之后的问题
这是一个坑,升级之后还是有死锁问题,一个新的死锁
由于 DelayedProduce 和 GroupMetadata 导致的死锁
1 | Found one Java-level deadlock: |
bug:
https://issues.apache.org/jira/browse/KAFKA-5970
修复版本:
0.11.0.2, 1.0.0
在 0.10.X 版本中没有看到有修复该问题,且在0.11.x版本中又看到有内存泄漏问题,~~~
好在这个问题出现的概率非常低,只在测试环境出现过1次