一个kafka的辛酸填坑路

2022-08-23 14:42:39 浏览数 (1)

一.前言

kafka是一个分布式的,支持多分区、多副本,基于 Zookeeper 的分布式消息流平台,它同时也是一款开源的基于发布订阅模式的消息引擎系统

博主所在的部门就是使用kafka做消息中间件,前不久碰到了一个奇奇怪怪的bug,找问题还花了不少功夫,特此在这记录一下。

二.bug起源

我所负责的模块里面有一个功能是修改宿主机的网络ip。

功能页面如下

输入IPV4地址后,就会将服务所在的宿主机地址给修改掉。

先说一下背景:测试环境所有服务均布在一台机器上,修改ip之后需要同步修改nacos配置中心的配置,各个中间件读取系统环境变量的配置等。然后整机重启,重启完成后,自动启动各个业务应用与中间件的docker容器。

从需求上来看,逻辑实现比较简单,java程序调用shell脚本去做一些宿主机上的操作,然后重启机器就好了。

功能代码咔咔一顿写。

写完之后一测试,ifconfig看一下ip已经修改,docker ps看一下容器都正常启动了,前端页面简单测试一些功能,都正常。

美滋滋啊,我果然是一个代码天才~

提测后,测试小姐姐给我说系统的操作记录一直没有生成,操作记录都是统一拦截发送到kafka,由专门的日志模块去消费记录到es。

淦!这功能也是最近刚开发的,刚刚改完一轮bug,自测过没有问题了。看了看代码,最后一次这个模块的提交记录也是我。

奇怪了。

看一下后台的日志,疯狂打印WARN日志

代码语言:javascript复制
1 partitions have leader brokers without a matching listener...

我就进到kafka的容器里面看了一下服务的消费情况

代码语言:javascript复制
kafka-consumer-groups.sh --bootstrap-server 127.0.0.1:9092 --group baiyan-topic --describe

看到消费情况为比较滞后,再去看一下卡主的topic的数据是什么

代码语言:javascript复制
kafka-console-consumer.sh --bootstrap-server 127.0.0.1:9092 --topic baiyan-topic --offset 373159  --partition 0 

数据为

代码语言:javascript复制
{省略业务字段,"createTime":"2021-10-20 16:27:39.447","updateTime":"2021-10-20 16:27:39.447"}

what?

这个时间怎么好像跟我更新测试环境IP的时间这么接近?

大量的消息积压在这个时间点无法被consumer处理到!

三.找问题

3.1.google报错

为了确定这个bug就是修改ip导致的,我让测试小姐姐在另外一台机器上全量模拟安装了一套环境,我又去修改了一下ip,果然又出现了这个问题。

ok,到这里,我们掌握的bug来源或者解决问题的入口就只有两个。

1.报错信息: 1 partitions have leader brokers without a matching listener 2.修改ip: 修改ip后,kafka消息堆积,无法消费,从提示信息来看是分区没有了lead节点导致。

好了,解决bug开始,第一步,google,哈哈哈哈~

在google一顿搜索:1 partitions have leader brokers without a matching listener

网上给出的答案大致集中在:

  1. kafka分区所在的服务器down了,rebalance失败。
  2. 防火墙没有关闭,导致网络不通。
  3. 代理ip配置不正确。

一个个看

第一点: 看了kafka的容器,启动正常,且如果新增一个topic,也能正常发送与消费,排除!

第二点: 防火墙未开启,哪怕开启了,端口也是通的,排除!

第三点: 从google来看,应该是这个答案了。

看一下配置文件的内容

代码语言:javascript复制
cat /opt/kafka/config server.properties 

与ip有关系的参数都很正常,已经修改成目标ip值,并且往上搜索到的配置都是注释的

我们使用的是比较早期的配置

代码语言:javascript复制
advertised.host.name=目标ip

但是走投无路,我还是抱着试一试心态,按照网上的操作去修改了配置,重启。

结果跟预想的一样,完全没用。

3.2.切换ip入手

一顿google,最后搜索出来的也都是说kafka集群搭建配置与网络问题,与3.1的搜索内容基本一致。

放弃。

3.3.原理入手

当时尝试使用3.1与3.2这两个线索搜索到的信息去解决问题无果后,还是有点难受的。不知道从哪里入手了。

后来想了想,kafka与消费者直接的交互,kafka的集群信息等同步信息,都是维护在zookeeper上的。

好,一个个来,一个个看。

先看一下topic的情况

代码语言:javascript复制
bash-4.4# kafka-topics.sh  --zookeeper 127.0.0.1:2181 --topic baiyan-topic --describe    
​
Topic: baiyan-topic   PartitionCount: 1       ReplicationFactor: 1    Configs: 
Topic: baiyan-topic   Partition: 0    Leader: none    Replicas: 1001  Isr: 1001

好了,这里发现一个端倪,leader为none,但是并没什么L用,从报错也能看出来。

继续到zookeeper的容器里面看一下kafka的信息

进入到bin目录

1.查看分区情况

代码语言:javascript复制
ls /brokers/topics/baiyan-topic/partitions
​
[0]

2.查看broker的id情况

代码语言:javascript复制
ls /brokers/ids
​
[1002]

至此,大家发现一些不一样的地方没有?

zookeeper上broker的id已经变成了1002,但是kafka里面topic信息副本与Isr的节点信息还是1001

kafka需要从zookeeper上获取到broker的节点信息来构建集群,kafka无法在zookeeper上找到1002节点,因此leader为none,无法构建集群。

ok,至此已经知道了导致这个bug出现的原因是什么了~

3.4.原因剖析

3.3.我们已经知道了导致消息阻塞的原因是什么了。那么导致zk与kafka上broker信息不一致的原因是什么呢?

突破口其实也挺清晰的了,broker不一致,那就从决定brokerId生成的几个因素去看一下。

引用一下《深入理解kafka》作者朱小厮大佬的博客:Kafka参数broker.id详解

由上可知,决定brokerId主要来源于两个文件,server.propertiesmeta.properties

先看一下server.properties配置

-1则为自动分配,默认配置从1001开始起跳,符合kafka的topic信息为1001。

再看一下meta.properties

存在于server.properties的配置log.dirs目录下,查看配置

代码语言:javascript复制
cluster.id=uHTKS_74RhW2_wKwbuwHxQ
version=0
broker.id=1002

好家伙,找到问题了!!!!

脚本修改ip后,修改了log.dirs,新生成了一个数据目录。重启kafka后,原先的topic内部的brokerid并未修改。而zk上,只要kafka节点下线了,1001节点数据被抹除,kafka重启后,新的log.dirs的数据目录生成。又因为server.properties上brokerId配置的是-1,由kakfa进行自增,从1001增加到1002,写入到的log.dirs目录下,并将1002节点注册到zk上,最终到了kafka与zk上互相不一致。

四.解决问题

知道了原因之后,解决问题的思路也就很清楚了嘛,只要保证ip修改后,新生成数据目录的brokerId与topic中brokerId保持一致即可。

方法一:

还原新的log.dirs目录下的meta.properties的broker.id属性为1001,在重启kafka,同步1001至zk。

太蠢了,治标不治本,集群模式下都不知道每个节点的id,需要人为介入。

方法二:

每个kafka节点指定server.properties内broker.id的值,不进行动态生成。

五.总结

本文分析kafka在宿主机修改了ip后,consumer无法消费到节点数据的问题。由浅入深的讲述了bug的排查过程。最终定位到broker.id不一致的bug。

我们因为测试环境是单机的,并没有指定broker.id,动态生成id导致了bug。

其实最终解决bug是比较简单的,改一下配置,重启就好了,但是排查过程比较艰辛。

这里回想起面试的时候应该也有不少的面试官会问kafka一些原理性的八股文,回头来想想也不是没有道理。

这里我强烈建议大家在使用的kafka的时候,不论是单节点的kafka还是kafka集群,都指定每个节点的id, 避免出现一些匪夷所思的bug。

0 人点赞