上一篇【Kafka】原理分析:分区副本机制我们介绍了什么是分区副本机制,里面提到了副本的协同,那么副本间数据同步的具体过程是什么样呢?本篇我们就一起来看看…
Producer在发布消息到某个Partition时,
get /brokers/topics//partitions/2/state
,然后无论该Topic的Replication Factor为多少(也即该Partition有多少个Replica),Producer只将该消息发送到该Partition的Leader。acks参数配置表示producer发送消息到broker上以后的确认机制。
public static final String ACKS_CONFIG = "acks"; // 通过ProducerConfig.ACKS_CONFIG进行配置
初始状态下,leader和follower的HW和LEO都是0,leader副本会保存remote LEO(远程副本的LEO),表示所有follower LEO,也会被初始化为0,这个时候,producer没有发送消息。follower会不断地给leader发送FETCH 请求,但是因为没有数据,这个请求会被leader寄存,而寄存时会出现两种情况:
对于Producer发送消息后的数据同步,也会出现两种情况:
这两种情况有着不同的处理方式,下面我们一起来详细的看看。
leader处理完producer请求之后,follower发送一个fetch请求过来。
1). 生产者发送一条消息leader副本:
2). 第一次fetch,同步log
3). 第二次fetch,更新HW
到目前为止,数据的同步就完成了,意味着消费端能够消费offset=1这条消息。
前面说过,由于leader副本暂时没有数据过来,所以follower的fetch会被阻塞,直到等待超时或者 leader接收到新的数据当leader收到请求以后会唤醒处于阻塞的fetch请求。处理过程基本上和前面说的一致
kafka使用HW和LEO的方式来实现副本数据的同步,本身是一个好的设计,但是在这个地方会存在一个数据丢失的问题,当然这个丢失只出现在特定的背景下。回想一下,HW的值是在新的一轮FETCH 中才会被更新。我们分析下这个过程为什么会出现数据丢失。
只有同时满足以下两种情况,才有可能出现数据丢失:
表达的含义是,至少需要多少个副本同步才能表示消息是提交的, 所以,当 min.insync.replicas=1 的时候,一旦消息被写入leader端log即被认为是“已提交”,而延迟一轮FETCH RPC更新HW值的设计使 得follower HW值是异步延迟更新的,倘若在这个过程中leader发生变更,那么成为新leader的 follower的HW值就有可能是过期的,使得clients端认为是成功提交的消息被删除。
1). leader副本本来的LEO=1,现在Producer又发来了一条消息
此时leader副本LEO=2
第一次fetch,follower副本更新LEO=2
第二次fetch,leader更新HW=2,remote LEO=2,
而此时,follower副本在收到响应 response(leader HW=2)前宕机了…
2). follower副本恢复,为了数据一致性,首先根据原有的HW=1进行了数据截断,删除了offset=2的消息。这时,在发送fetch请求前leader宕机了…
3). leader副本恢复,但此时follower副本已经成为leader,
===> 最终offset=2的消息丢失,其实问题关键就在于follower副本恢复之后进行了截断
在kafka0.11.0.0版本之后,引入了一个leader epoch来解决这个问题,实际上是一对值(epoch,offset)
比如 (0,0), (1,50) ,表示第一个leader从offset=0开始写消息,第二个leader版本号是1,从 offset=50开始写;从而可以推出第一个leader写了50条消息。
这个信息会持久化在对应的分区的本地磁盘上,文件名是 /tmp/kafkalog/topic/leader-epoch-checkpoint
。leader broker中会保存这样一个缓存,并且定期写入到checkpoint文件中,当leader写log时它会尝试更新整个缓存:
基于同样的情况来分析,follower宕机并且恢复之后,有两种情况:
Kafka提供了数据复制算法保证,如果leader副本所在的broker节点宕机或者出现故障,或者分区的 leader节点发生故障,这个时候怎么处理呢? kafka必须要保证从follower副本中选择一个新的leader副本。
KafkaController会监听ZooKeeper的/brokers/ids节点路径,一旦发现有broker挂了,执行下面的逻辑。这里暂时先不考虑KafkaController所在broker挂了的情况,KafkaController挂了,各个 broker会重新leader选举出新的KafkaController
若leader副本挂了,leader副本在该broker上的分区就要重新进行leader选举,目前的选举策略是:
优先从isr列表中选出第一个作为leader副本,这个叫优先副本,理想情况下有限副本就是该分区的leader副本
如果isr列表为空,则查看该topic的unclean.leader.election.enable配置。
unclean.leader.election.enable为true,代表允许选用非isr列表的副本作为leader,那么就意味着数据可能丢失
注:此时会从其他副本中选出一个作为leader副本,并且新的isr列表只包含该leader 副本
unclean.leader.election.enable为false,表示不允许,直接抛出NoReplicaOnlineException异常,造成leader副本选举失败。
一旦选举成功,则将选举后的leader和isr和其他副本信息写入到该分区的对应的zk路径上。