Kafka 基础知识-02

目录

Kafka Broker介绍

1、Kafka Broker工作流程

(1)zookeeper存储的kafka信息

(2)Kafka Broker总体工作流程

(3)broker上下线模拟

(4)Broker重要参数

2、节点服役和退役

(1)节点服役

(2)节点退役

3、Kafka的副本

(1)leader选举流程

(2)leader和follower故障处理细节

(3)分区副本分配

(4)生产经验

4、文件存储

(1)topic数据的存储机制

(2)kafka的文件清理策略

5、kafka如何实现高效读写数据(面试重点)


Kafka Broker介绍

本节将详细介绍kafka cluster内部是如何存储数据的,以及zk与kafka之间是如何沟通数据的。

1、Kafka Broker工作流程
(1)zookeeper存储的kafka信息

zkCli.sh

ls /kafka

可以看出/kafka节点下记录了很多很多信息,我们一个一个ls,get不太方便,此时可以使用一个zookeeper的图形化工具prettyZoo,它可以实现以图形化的方式查看各个节点的信息。

下载地址:https://github.com/vran-dev/PrettyZoo/releases

然后点击安装包自行安装即可,打开prettyZoo连接上zk服务器就可以使用了。

在zookeeper的服务端存储了以下kafka的相关信息:

其他节点下的数据我们暂时不关心。

(2)Kafka Broker总体工作流程

在启动kafka之前,必须保证zk启动成功。启动kafka集群的时候,每台broker启动成功,都会在zk的/kafka/brokers/ids节点下注册一个新节点,表示自己启动成功了。每个broker内部都会有一个Controller监听器,只要broker启动成功这个Controller监听器就会争抢去/kafka节点下创建/kafka/controller节点,并把自己的信息写进入,谁抢先注册/kafka/controller节点,谁就负责辅助后续leader的选择。显然,最先启动的broker肯定会优先抢到/kafka/controller节点的创建,其对应的Controller监听器被选出来用于辅助后续leader选择。接下来由选举出来的Controller监听/kafka/broker/ids节点的变化,有任何broker故障了或挂掉了它都可以监听到。

此时Kafka集群刚刚启动,其内部还没有创建任何主题。当我们创建主题的时候Controller会进行启动时的leader选举(初始化操作)。

选举规则如下:在isr中存活为前提,按照AR(又称Replicas,AR表示kafka分区中所有副本的统称)记录的顺序进行轮询。例如ar记录了[1, 0, 2],isr记录了[1, 0, 2],优先轮询broker1,且broker1是存活的,因此broker1内存储的副本被作为leader。注意:ar和isr在创建topic时,会为每个partition自动生成,初始生成的ar = isr且不同分区每位都尽可能不同,以保证后续为不同分区选leader时选出的leader尽可能在不同broker上,保证负载均衡。好比上面我们创建的first主题有三个分区,初始生成的ar和isr分别为201,012,120。

当leader选举出来以后,Controller会将选出的leader和isr信息注册给zookeeper,然后其他所有Controller会从zookeeper中同步相关信息,防止被选出来的Controller挂掉。如果Controller老大挂掉,即/kafka/controller节点就会删除,其他所有的Controller都会监听/kafka/controller节点的变换,一旦监听到/kafka/controller节点被删除,那么其他Controller又会重新抢占。

leader选完后,kafka集群就可以接收生产者发来的数据了。生产者会先与zk通信获得谁是leader,然后给leader发数据,leader再进行数据同步。数据同步过程中会产生同步信息,同步信息是以segment片段(可以理解为日志)的方式存储的,同时为了后续能快速查找信息,还为其设置了index索引。

假设当前broker1挂掉了,/brokers/ids/节点下1号节点就没了(0,1,2节点都是临时节点),Controller就会监听到节点的变化。topic-first-partition-0的leader在broker1中,因此Controller会对partition0进行运行时leader选举。选举规则如下:首先取/brokers/topics/first/partitions/0/state节点存储的isr信息,然后在isr存活为前提下,按照ar记录的顺序轮询。例如ar记录了[1, 0, 2],isr记录了[0, 2],broker0内存储的副本被重新选为leader。新leader选出以后Controller会将新选出的leader和isr信息更新给zookeeper,然后其他所有Controller再次从zookeeper中同步相关信息。后续生产者就可以与新leader进行数据通信了。

(3)broker上下线模拟

目前所有broker都在线,且broker0上的Controller被选为老大

停止node33上的kafka:kafka-server-stop.sh,再次查看信息:

重新启动后查看ids节点

注意:如果某个leader或follower挂掉了,各主题各分区下存储的ISR会立刻监听到(/kafka/brokers/topics/主题/partitions/分区/stat),并将其剔除掉。前面讲过的超时阈值replica.lag.time.max.ms默认30s,这个参数是当follower存活的前提下,可能由于网络的原因或其他原因长时间未向leader发送通信请求或同步数据,此时该参数才生效。

(4)Broker重要参数

2、节点服役和退役
(1)节点服役

节点服役是指为现有的kafka集群添加新的broker。在生产环境中需要提前准备好Linux服务器,然后配置好所有的前置环境(jdk,ip地址等等),安装配置好kafka启动即可(不需要停止现有的kafka集群)。现在我们为了省去前置环境的配置克隆一下现有的broker。

关闭node33,并右键执行克隆操作,将克隆出的新服务器作为node44

先开启node44修改ip地址和主机名,防止ip冲突:

vim /etc/sysconfig/network-scripts/ifcfg-ens33

vim /etc/hostname node44

然后重启node44:reboot,并启动node33,修改 node44中 kafka 的 broker.id 为 3并删除 node44 中 kafka 下的 datas 和 logs。因为这里的datas和logs是node33的信息,如果不删除会导致node33启动kafka后node44的kafka下线,启动node44的kafka后node33的kafka下线。

注意:添加完node44后一定要给各个服务器添加node44的主机名映射,kafka的连接必须通过主机名才可以连接。建议将linux各个服务器,以及windows本机都设置好node44的主机名映射。如果不配置后续实验会报node44无法连接错误。

至此node44就配置好了,在生产环境中node11,22,33的kafka集群是正在运行中的,此时直接单独开启node44的kafka服务即可将其加入到node11,22,33的kafka集群当中,完成kafka集群的节点扩展。

添加完新节点后,再次查看历史的topic信息:

发现历史的topic并没有因为新节点的到来而产生任何变化,显然这不是我们想要的,我们添加新节点的目的就是为了缓解旧节点的压力,那现在该怎么办呢?此时需要执行负载均衡操作,使某个主题重新初始化。

cd /export/server/kafka

vim topics-to-move.json    # 设置为那个主题执行负载均衡

当然可以设置多个主题{"topic": "first"},{"topic": "xxx"},{}....

然后生成负载均衡计划:(只是生成了计划,还没有具体去执行,生成计划后如果我们感觉满意,就可以执行了,如果不满意可以重新生成)(--broker-list "0,1,2,3"注意,后不加空格)

查看了执行计划后感觉还行就可以执行了,将执行计划复制粘贴到副本存储计划文件中。创建副本存储计划文件:vim increase-replication-factor.json。然后执行:

(2)节点退役

节点退役是指将kafka集群中的节点退出当前集群环境,例如上述集群有node11,22,33,44,节点退役就是将其中一台服务器退出kafka集群,我们以退出node44为例。退出节点其时很简单直接关了不就可以了,但是在生产环境中要退出的节点node44(broker3)必然存储了一部分分区副本(比如上述first主题),直接关闭显然会破坏数据。因此在关闭节点之前需要将相应的数据迁移(reassignment)给其他节点(原理就是重新执行负载均衡对已有的主题reassignment)。

 vim topics-to-move.json    # 设置为那个主题执行负载均衡

创建执行计划,不使用broker3:

同理将计划存放到vim increase-replication-factor.json文件中,然后执行计划:

显然first主题各个分区的数据已经完全不使用broker3存储了,此时broker3就可以直接关闭了:kafka-server-stop.sh。至此node44退役成功

3、Kafka的副本

Kafka默认副本数是1个生产环境中一般配置2个,保证数据可靠性;太多副本会增加磁盘空间的消耗,增加网络上数据的传输,降低效率。Kafka分区中的所有副本统称为AR = ISR + OSR。其中ISR表示leader本身和与leader保持正常同步的follower的集合,如果某follower长时间(默认30s)不与leader通信,则ISR会踢出该follower。如果leader发生故障,系统就会从ISR中重新选举出leader。OSR表示follower与leader副本同步时,延迟过多的副本(超时的副本),ISR中踢出的follower就存储在OSR中了。

(1)leader选举流程

验证:先创建一个新的topic,4个分区,4个副本(先把node44重新启动添加到kafka集群中

停止node44的kafka进程:kafka-server-stop.sh,模拟broker3挂掉,并查看leader分区情况:

停止node33的kafka进程:kafka-server-stop.sh,模拟broker2挂掉,并查看leader分区情况:

启动node44的kafka进程,模拟broker3修复,并查看leader分区情况:

启动node33的kafka进程,模拟broker2修复,并查看leader分区情况:

停止node22的kafka进程:kafka-server-stop.sh,模拟broker1挂掉,并查看leader分区情况:

(2)leader和follower故障处理细节

follower故障处理细节:

LEO(Log End Offset)表示每个副本最后一个数据的下一位。HW(High Watermark)表示所有副本中最小的LEO(<HW的数据是经验证过的数据)例如当前broker0有A,B,C,D,E这些数据,offset表示消息位移,即分区中每条消息的位置信息,例如数据C的offset为2,就是index索引,offset是一个单调递增且不变的值。leader会直接和生产者通信,然后follower主动拉去leader数据进行数据同步,显然leader先获取数据follower后获取数据,即leader存储的数据比follower多。数据同步过程有延迟不同的follower在同一时间获取的数据也不同,有长有短。

此时如果follower挂了,首先会被ISR踢出去,我们假设broker2挂了:

然后其他没挂的leader和follower继续正常接收和同步数据:

follower不可能一直故障,待该follower恢复之后,follower会读取本地磁盘记录的上次HW,并将高于HW的部分(>=HW)截取掉(它认为这些数据是没有经验证的数据),从HW开始与leader进行同步:(注意该follower恢复以后不会立刻重新加入ISR,目前还没有加入ISR)

等待该follower的LEO大于等于该Partition的HW,即该follower同步的速度赶上leader之后,才会重新加入ISR。至此follower成功恢复。

leader故障处理细节:

假设leader挂掉了,此时会将其踢出ISR,然后Contrallor会重新选出一个新的leader:

为了保证多个副本之间数据的一致性,其余follower会先将各自>=HW的部分截取掉(leader是直接与生产者通信的,follower的数据不能比leader还多),然后从新的leader中同步数据:

注意:这里只能保证副本之间的数据一致性,并不能保证数据不丢失或不重复。

(3)分区副本分配

如果kafka服务器只有4个broker,那么设置kafka的分区数大于服务器的台数时,kafka的底层是如何分配存储副本的呢?

首先重新创建一个topic:second,16个分区,3个副本

纵向看:

注意:除了首位是随机的之外,第一组黄色标记1230错开1位,错开的位数不一定都是从1位开始,这与我们创建的上一个topic有关。

横向看: 

这样做的目的是尽可能的保证副本数量上的负载均衡,比如上述每个broker存储9个副本(缺点是不考虑各服务器性能好坏)

(4)生产经验

手动调整分区副本存储:在生产环境中,每台服务器的配置和性能不一致,但是默认情况下kafka不会考虑这些问题,它只会根据自己的规则均匀的创建对应的分区副本(保障在数量上的负载均衡),这就会导致个别性能差的服务器需要处理的任务量与性能好的服务器一样。这显然不符合实际情况,实际开发中肯定是能者多劳,性能好的服务器存储的副本多点,性能差的服务器存储的副本少点,此时就需要我们手动的调整分区副本存储。

假设当前有4台服务器broker0,1,2,3。创建一个新topic:three,4个分区2个副本,将该topic的所有副本都存储在性能好的broker0和1上。

首先创建主题three:(初始的分区副本存储首位都是随机的,其他位错开几位也不是一定从错开1位开始,这里会受到以前创建topic的影响,在创建topic:second时蓝色标记各组分别错开2位,3位,1位,2位,因此在创建three主题时会衔接上,开始错开3位)

显然可以看出每个broker都负责存储两个副本,只做到数量上的负载均衡。(然后修改topics-to-move.json中的主题为three,生成执行计划--broker-list只添加0,1)按理来说需要执行橙色字体的步骤,但是为了模拟后续leader partition不均衡的情况方便下一小节讲解leader partition,我们直接输入下面自己定义的执行计划。创建副本存储计划,让所有副本都存储在broker0和broker1上:vim increase-replication-factor.json

执行副本存储计划:

可以看出目前所有的副本都存储在broker0和broker1上了。原来各个分区的leader分别是2,3,1,0。由于我们不使用broker2,3存储副本了。因此partition0和partition1需要重新leader选举。对于partition0,它的新AR为[0, 1],broker0被选为了leader。对于partition1,它的AR为[0,1],broker0被选为leader。对于partition2和3,leader分别是1,0后续还会使用。因此leader不变。最终各分区的leader变为了0,0,1,0。

自己测试:(使用系统生成的执行计划,可能已经leader partition负载均衡了,当然这里我们运气好还是没有均衡)

leader Partition负载均衡:

正常情况下,kafka本身会自动把leader Partition均匀分散在各个机器上,来保证每台机器的读写吞吐量都是均匀的。但是如果某些broker宕机,会导致leader Partition过于集中在其他少部分几台broker上,这会导致少数几台broker的读写请求压力过高,其他宕机的broker重启之后也都会变成follower partiton,此时会照成leader partition负载不均衡。比如上述各个分区的leader分别是0,0,1,0显然leader partition不均衡,broker0的压力过大。又比如将我们自己测试过程中的broker1挂掉,此时各个分区的leader是0,0,0,0。显然broker0压力过大,即使broker1恢复leader也不会发生变化。

解决方法:auto.leader.rebalance.enable    :默认为true,自动leader partition平衡

leader.imbalance.per.broker.percentage: 默认是10%,每个broker允许的不平衡的leader比例。如果每个broker超过这个阈值,控制器就会触发leader的再平衡。

不平衡比例计算方法,假设集群中有如下一个主题:

显然各个分区的leader:0123与replicas的第一位3102不同,说明当前主题出现过节点故障或宕机的事件,leader重新选举过,导致它们两者不一致。那既然出现过节点宕机就有可能发生leader partition不均衡,系统会计算其不平衡率查看是否需要leader partition再平衡。计算规则如下:

针对broker0节点,partition2的AR优先副本是0节点,但是0节点却不是leader,所以不平衡数+1,AR副本总数是4,因此broker0节点的不平衡率为1/4 = 25% > 10%,需要再平衡。同理broker2,3的不平衡为25%,需要再平衡。broker1的不平衡率为0,不需要再平衡。

leader.imbalance.check.interval.seconds默认值30s,检查leader负载是否平衡的时间间隔。

思考:明明上面各分区的leader分别在broker0,1,2,3上是平衡的呀,为什么系统会认为不平衡呢?因为这里只是讲了一个特例。上述规则在面临真正不平衡时,可以做到再平衡,如下图所示,对于broker0不需要再平衡。对于broker1显然分区3的AR是[1,0],broker1在前面但是它不是leader,不平衡率为50%需要在平衡,平衡后各分区的leader就变为了0011。但是这种规则会将某些本身平衡的leader partition视为不平衡,这是这种规则的弊端。

因此在生产环境中我们一般将auto.leader.rebalance.enable为false,防止它误判,执行leader partition再平衡会消耗大量系统性能,只有我们明显的发现当前的topic有明显leader partition不平衡时才考虑开启。或者开启auto.leader.rebalance.enable,将leader.imbalance.per.broker.percentage设置的高点,例如20%,30%,避免频繁的leader partition再平衡,影响系统性能。

测试:下面是我们目前生成的topic : three

按理来说leader分别是0110,显然已经负载均衡了。但是kafka的leader Partition负载均衡策略会认为不均衡,broker0的不平衡率为50%,broker1的不平衡率为50%,因此等待一段时间后会触发leader Partition负载均衡,过一段时间再次查看topic three:

因此一般将auto.leader.rebalance.enable为false,防止它误判,影响系统效率。

增加副本因子:在生产环境中,由于某个主题特别特别的重要,因此需要增加副本数,确保该主题数据不丢失。副本数的增加需要先制定计划,然后根据计划执行。

场景有一个主题four开始创建时不太重要,因此设置了只有1个副本,随着时间的推移,该four主题越来越重要了,需要对其进行增加副本数。

首先创建topic:four:

注意接下来必须手动设置不能让系统生成,系统生成的执行计划还是按副本数为1生成的如下图:

然后手动设置副本存储计划:vim increase-replication-factor.json,输入如下内容:

最后执行即可:

验证:

结果:

4、文件存储
(1)topic数据的存储机制

topic是逻辑上的概念,而partition是物理上的概念,每个partition对应于一个log文件,该log文件中存储的就是Producer生产的数据。Producer生产的数据会被不断追加到该log文件末端,为防止log文件过大导致数据定位效率底下,kafka采取了分片和索引的机制,将每个partition分为多个segment(数据真正物理上的概念)。每个segment包括:“.index”文件、“.log”文件和".timeindex"等文件。这些文件位于一个文件夹下,该文件夹的命名规则为:topic名称+分区序号,例如:first-0。

一个topic分为多个partition,一个partition分为多个segment,每个segment由.log文件.index.文件timeindex文件以及其他文件组成。.log文件是用于存储真实数据的日志文件。.segment默认1G大小,1G大小的文件查询起来也比较慢,.index文件就是用于加快查询效率的。.timeindex文件是时间戳索引文件,我们知道当一个消费者消费完数据后kafka是不删除数据的,方便其他消费者使用。但是这些数据总不能一直不删除吧,那数据慢慢累积的越来越多而不处理,会导致系统性能下降,甚至宕机,这显然是不行的。实际上Kafka默认只保存数据7天,7天前的数据会自动删除。那如何判断kafka集群中的数据超没超过7天呢?.timeindex文件就记录了当前segment中消息的创建日期,这样kafka集群就能够知道那些segment存储时间超过了7天,当超时时,系统自动删除(后面会具体讲)。

.log.index.timeindex文件是以当前segment的第一条消息的offset命名的。由于Producer生产的数据会被不断追加到该log文件末端,意味着历史的数据不进行修改,这种方式读写速度较快。注意在配置kafka的server.properties文件时,我们配置过log.dirs=/export/server/kafka/datas,这就是kafka数据(topic数据)的存储位置。

首先启动生产者,并发消息:

查看node11,22,33任意一个节点的/export/server/kafka/datas/first-0(first-1,first-2)目录:

为什么无法直接查看呢?因为这些文件都是序列化之后的文件。那我就想要看看里面的内容该怎么办呢?kafka本身提供了一个查看index,log文件的工具,我们通过以下命令可以查看文件数据。

注意.index是稀疏索引,并不是每一个消息就写一个索引。默认每4k消息写一条索引,显然上述first-0中.log文件不足4kb,因此.index只写了一条索引。此外.index文件中保存的offset为相对offset,这样能确保offset的值不会过大,offset的值过大查询时也会影响效率。

思考:这种索引机制怎么定位数据呢?

(2)kafka的文件清理策略

kafka中默认的日志保存时间为7天,可以调整以下参数修改保存时间:

log.retention.hours  :小时,默认168,7天  (最低优先级)

log.retention.minutes  : 分钟,默认为null

log.retention.ms  :毫秒,默认为null   (最高优先级)

如果设了log.retention.minutes则log.retention.hours无效

log.retention.check.interval.ms  :检测周期,默认5分钟。注意:检测周期设置的时间一定要小于日志保存时间,否则日志保存时间设置的没意义。

思考:一旦日志超过了日志保存时间,kafka怎么处理呢?kafka提供了两种日志清理策略:delete(默认)和compact

1)delete日志删除:将过期数据删除,log.clenup.policy=delete(所有数据启用日志删除策略)

        delete策略下又分为两种策略:

        A:基于时间(默认开启)。以segment中所有记录中的最大时间戳作为该文件的时间戳。(以最后一个消息的时间戳为准)

        B、基于大小(默认关闭)。log.retention.bytes会设置所有log日志允许存储数据的总大小(默认-1,表示无穷大),超过该大小会删除最早的segment。这种设置的目的是怕系统硬盘不是很大,因此将log.retention.bytes设置为硬盘大小,硬盘如果存不下了就删除最老的segment。生产环境中一般硬盘都很充足,因此不开启。

2)compact日志压缩:这里的压缩与以前所学的snappy,zip不一样。它是将相同key的不同value值,只保留最后一个版本log.cleanup.policy=compact   :所有数据启用压缩策略

注意:压缩后的offset可能是不连续的,比如上图中没有6,当从这个offset消费消息时,将会拿到比这个offset大的offset对应的消息,实际会拿到offset为7的消息,并从这个位置开始消费。

这种策略只适合特殊场景,比如消息的key是用户id,value是用户的资料,通过这种压缩策略,整个消息集里就保存了所有用户最新的资料。

compact策略用的比较少,一般都是使用delete

5、kafka如何实现高效读写数据(面试重点)

A、kafka本身是分布式集群,可以采用分区技术,并行度高。

B、读数据采用稀疏索引,可以快速定位要消费的数据。

C、顺序写磁盘:kafka的producer生产数据,要写入到log文件中,写数据的过程一直是追加到文件末端,为顺序写方式。官方有数据表明同样的磁盘,顺序写能到600M/s速度,而随机写只有100K/s。这与磁盘的机械结构有关,顺序写之所以快,是因为其省去了大量磁头寻址的时间。

D、页缓存+零拷贝技术

我们知道kafka中topic的数据是存储在linux系统下的(/export/server/kafka/datas)。这个文件是linux的磁盘文件File,生产者发来数据后,这些数据会直接交给linux系统内核kernel space,内核会尽可能的将数据存储在页缓存PageCache中(在内存中)。当消费者读数据时,先从PageCache中查找,如果找到了哪效率会极高。如果没找到再从磁盘中读取,总之有了页缓存总有一定的命中率,效率提升了(涉及到操作系统)。

零拷贝技术就是:kafka的数据加工处理,发送数据,消费数据的操作交由kafka生产者和消费者处理。kafka Broker应用层不关心存储的数据,只要生产者存进来后续kafka Broker就不管了,所以消费者消费时不走应用层,传输效率高。(Kafka生产者发送数据先进行seng-->拦截器-->序列化-->分区器-->缓存队列-->sender线程-->kafka集群,这就是零拷贝技术的体现,发送数据的过程kafka Broker不参与,同理kafka 消费者里面有反序列化.....等等过程)

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值