Kafka运行一段时间后,随着topic, partition越来越多,会发现一个现象:有的时候需要做一些运维变更,重启起来特别慢。
于是就像分析一下Kafka启动时间主要消耗在哪里,看看有没有调优空间。
就从KafkaServer的startup方法逐行的看下去吧:
initZk
主要是连接zookeeper, 然后在zookeeper上初始化Kafka需要的一些永久节点。这里有个注意的是,如果我们有多套Kafka集群,我们是可以公用一套Zookeeper集群的,而没有必要每个Kafka集群私自搭建一套Zookeeper,但是Kafka默认是将这些永久节点创建在Zookeeper的根路径,所以如果要公用Zookeeper那最好就将不同的Kafka集群放到不同的路径下。配置也很简单:zookeeper.connect=127.0.0.1:2181/kafka1,那Kafka启动的时候就会将自己的永久节点创建在/kafka1下面。
不过按照这种方式的配置有多一步连接Zookeeper和判断/kafka1节点是否存在的开销,这个时间一般不会很长。
init logManager
对于运行一段时间的集群,这一步是启动时最耗时的。主要时间消耗在LogManager里的loadLogs。这里会把所有的日志目录打开,然后“处理”每个partition里的每个LogSegment。
这里简单的描述一下Kafka的目录结构:Kafka通过log.dirs可以配置多个日志目录,比如你有多块磁盘,那么就可以将日志分散到这些磁盘里。然后每个目录下就是partition的目录,目录名称为 topic-partition id,比如 __consumer_offsets-12,那就是topic是__consumer_offsets,partition是12。然后在parition目录就有两类文件,以.log为后缀的消息内容的文件,以.index为后缀的逻辑offset到物理offset的映射文件。这些.log, .index文件的文件名也挺有意思,名称都是用逻辑offset来命名的,比如00000000000000000100.log。
在loadLogs里针对log.dirs里的每个目录都会开一个线程池来进行处理,这个线程池的线程数是由num.recovery.threads.per.data.dir这个配置来控制的,这个配置值默认是1,而且这个线程池仅仅是在启动的时候使用,所以即使配的很大也不会影响之后的Kafka的运行。所以这个值是影响启动很重要的因素。我们有个集群,有12个目录,然后将这个参数修改为2,也就是总共有24个线程来处理,Kafka的启动时间从5分钟降低到了2分钟。
再来看看处理过程中主要干了些啥呢,初始化Log,然后就是loadSegments。
删除后缀名为.deleted, .cleaned的文件
如果.index对应的.log文件不存在则将.index删除
打开一个个的LogSegment。这里有点要注意的是,Kafka启动的时候会把日志目录所有存在的.log, .index都给打开。这个大部分时候没有什么问题,但是我们之前也碰到一个故障:Too many open files。打开完.log和.index,会做一个简单的index检查。
这个检查算是一个基本的检查,.index文件里每一项是8个字节:4个字节的逻辑offset(real offset - baseOffset),4个字节表示消息在.log文件中的物理offset。那么如果.index文件是完整的,则用文件的大小应该能整除8。这个检查如果没有通过的话,那么就会将.index文件删掉,然后进行重建,需要将.log文件进行一次遍历,这个时间就有点长了。另外,如果.log文件存在,而.index文件不存在也需要重建。
大部分时候这样loadLogs的工作就算完成了,但是如果你停止Kafka的时候是非正常关闭,那么就会带来非常大的工作量了。什么叫非正常关闭呢?Kafka会检查每个log dir目录是否有一个名为.kafka_cleanshutdown的文件,如果存在则是正常停止。这是什么意思?在Kafka正常停止的时候,会做一些保存和清理工作,然后创建这样一个文件,所以如果要做正常维护的时候千万不要直接Kill -9 Kafka的进程哦。
领取专属 10元无门槛券
私享最新 技术干货