在Kafka中,每个topic都可以配置多个分区以及多个副本。每个分区都有一个leader以及0个或者多个follower。在创建topic时,Kafka会将每个分区的leader均匀地分配在每个broker上。我们正常使用Kafka是感觉不到leader、follower的存在的。
但其实,所有的读写操作都是由leader处理的,而所有的follower都是复制leader的日志数据文件,如果leader出现故障时,follower就会选举为leader。
所以说
leader职责 读写数据
follower职责 同步数据、参与选举(leader crash之后,会选举一个follower重新成为分区的leader)
答:
zookeeper的leader负责读、写;follower可以读取,但不能写
Kafka的leader 负责读、写;follower不能读写数据,而是作为每个消费者消费的数据一致的。
Kafka的一个topic可以有多个分区,一样可以实现数据操作的负载均衡。
在zk中会保存AR(Assigned Replicas)列表,其中包含了分区所有的副本,其中 AR = ISR+OSR
ISR(in sync replica):是kafka动态维护的一组同步副本,在ISR中有成员存活时,只有这个组的成员才可以成为leader,内部保存的为每次提交信息时必须同步的副本(acks = all时),每当leader挂掉时,在ISR集合中选举出一个follower作为leader提供服务,当ISR中的副本被认为坏掉的时候,会被踢出ISR,当重新跟上leader的消息数据时,重新进入ISR。可以理解为在follower集合中,有多少结点是存活的。
OSR(out sync replica): 保存的副本不必保证必须同步完成才进行确认,OSR内的副本是否同步了leader的数据,不影响数据的提交,OSR内的follower尽力的去同步leader,可能数据版本会落后。
在Kafka早期版本,对于分区和副本的状态的管理依赖于zookeeper的Watcher和队列:每一个broker都会在zookeeper注册Watcher,所以zookeeper就会出现大量的Watcher, 如果宕机的broker上的partition比较多,会造成多个Watcher触发,造成集群内大规模调整;每一个replica都要去再次zookeeper上注册监视器,当集群规模很大的时候,zookeeper负担很重。这种设计很容易出现脑裂和羊群效应以及zookeeper集群过载。
新版本该变了这种设计,使用KafkaController,只有Kafka Controller Leader
会向zookeeper上注册Watcher,其他broker几乎不用监听zookeeper的状态变化。
而Kafka集群中多个broker,每次只有一个会被选举为controller leader,负责管理整个集群中分区和副本的状态,比如partition的leader 副本故障,由controller 负责为该partition重新选举新的leader 副本;当检测到ISR列表发生变化,有controller通知集群中所有broker更新其MetadataCache信息;或者增加某个topic分区的时候也会由controller管理分区的重新分配工作。
当broker启动的时候,都会创建KafkaController对象,但是集群中只能有一个leader对外提供服务,这些每个节点上的KafkaController会在指定的zookeeper路径下创建临时节点,只有第一个成功创建的节点的KafkaController才可以成为leader,其余的都是follower。当leader故障后,所有的follower会收到通知,再次竞争在该路径下创建节点从而选举新的leader
replica.lag.time.max
就可以配置这个时间参数。这种方式解决了上述第一种方式导致的问题。导致副本follower和leader不同步的原因主要有3种情况:
一个partition的follower落后于leader足够多时,被认为不在同步副本列表或处于滞后状态。
正如上述所说,现在kafka判定落后有两种,副本滞后判断依据是副本落后于leader最大消息数量(replica.lag.max.messages)或replicas响应partition leader的最长等待时间(replica.lag.time.max.ms)。前者是用来检测缓慢的副本,而后者是用来检测失效或死亡的副本
何时提交,这与配置选项acks相关,
同步复制(acks=all): 只有所有的follower把数据拿过去后才commit,一致性好,可用性不高。
异步复制(acks=1): 只要leader拿到数据立即commit,等follower慢慢去复制,可用性高,立即返回,一致性差一些。
**Commit:**是指leader告诉客户端,这条数据写成功了。kafka尽量保证commit后立即leader挂掉,其他flower都有该条数据。
kafka不是完全同步,也不是完全异步,是一种ISR机制:
leader会维护一个与其基本保持同步的Replica列表,该列表称为ISR(in-sync Replica),每个Partition都会有一个ISR,而且是由leader动态维护
如果一个flower比一个leader落后太多,或者超过一定时间未发起数据复制请求,则leader将其重ISR中移除
当ISR中所有Replica都向Leader发送ACK时,leader才commit
既然所有Replica都向Leader发送ACK时,leader才commit,那么flower怎么会leader落后太多?
producer往kafka中发送数据,不仅可以一次发送一条数据,还可以发送message的数组;批量发送,同步的时候批量发送,异步的时候本身就是就是批量;底层会有队列缓存起来,批量发送,对应broker而言,就会收到很多数据(假设1000),这时候leader发现自己有1000条数据,flower只有500条数据,落后了500条数据,就把它从ISR中移除出去,这时候发现其他的flower与他的差距都很小,就等待;如果因为内存等原因,差距很大,就把它从ISR中移除出去。
面对replica宕机时,Kafka的处理方式主要有两种:
1、等待ISR中任一Replica恢复,并选它为Leader
2、选择第一个恢复的Replica为新的Leader,无论它是否在ISR中
Kafka采用的是拉取模型,由消费者自己记录消费状态,每个消费者相互独立地顺序拉去每个分区的消息。消费者可以按照任意的顺序消费消息。比如,消费者可以重置到旧的偏移量,重新处理之前已经消费过的消息;或者直接跳转到最近的为位置,从当前的时刻开始消费。