目录
了解学习别人的方案,不仅仅是掌握技术,其实在处理自己系统的问题和设计时,也会获得启发。所以今天打算聊一下kafka是如何进行数据复制,和failover是如何处理的。从下面几个方面阐述一下:
- 如何propagate消息
- 何时对消息进行commit
- replica是如何进行恢复的
- replica全部宕机如何处理
如何propagate消息
如图,假设目前kafka集群有4个broker节点,运行3个topic,他们的副本数为2,在创建topic的时候,kafka会尽可能均匀的分配各个topic的partition,达到负载均衡的目的。
每个topic的partition会存在对应的副本,也就是replica,在读写数据的时候都是通过leader进行,而且只能通过leader进行,follower会周期性的到leader上去pull数据,其实也就类似于一个consumer的角色。
何时对消息进行commit
为什么要说commit呢,复制和消息的commit又有什么关系?
一般我们在做分布式的写的时候,会有同步复制,和异步复制两种方式:
同步复制:写完leader之后,同时也要保证follower写入完成,leader只要返回,
就代表数据主副本都写入成功,可以保证强的一致性,但是会降低可用性。
异步复制:写完leader之后,leader就可以返回,告诉客户端数据写入成功,
然后follower异步的从leader获取最新数据,可以是leader异步push,也可以是follower异步pull。
而kafka不完全是这两种方式的一种,kafka的leader会维护一个ISR列表,根据列表和producer的ack配置来决定leader何时commit; 只有commit的数据才可以被consumer消费。下面结合ISR和commit策略详细解释。
ISR
ISR(in-sync replica),顾名思义,这就是一个正在保持同步的副本列表;
它的特点是当一个follower比leader落后太多,或者过长时间没有发起向leader的同步请求,follower就会从ISR中剔除。
commit策略
kafka的server有下面两个配置,控制着何时剔除ISR中的replica。
# 副本同步延迟十秒剔除
replica.lag.time.max.ms=10000
# 副本同步延迟消息数超过6000条剔除
replica.lag.max.messages=6000
kafka的topic可以通过min.insync.replicas=1
配置此topic最小的ISR列表数量。
最后结合producer配置request.require.acks
控制commit,这个参数实际上有三种值可以设置,分别是0,1和all。
- 第一种选择是把参数设置成0
kafkaProducer只要把消息发送出去,不管那条数据有没有在哪怕Partition Leader上落到磁盘,就不管他了,直接认为这个消息发送成功。如果你采用这种设置的话,那么你必须注意的一点是,可能你发送出去的消息还在半路。结果呢,Partition Leader所在Broker就直接挂了,然后结果你的客户端还认为消息发送成功了,此时就会导致这条消息就丢失了。
- 第二种选择是设置acks=1
只要Partition Leader接收到消息而且写入本地磁盘了,就认为成功了,不管其他的Follower有没有同步过去这条消息了。这种设置其实是kafka默认的设置方式,也就是说默认情况下,要是不设置这个参数,只要Partition Leader写成功就算成功。但是这里有一个问题,万一Partition Leader刚刚接收到消息,Follower还没来得及同步过去,结果Leader所在的broker宕机了,此时也会导致这条消息丢失,因为客户端已经认为发送成功了。
- 最后一种情况就是设置为all
Partition Leader接收到消息之后,还必须要求ISR列表里跟Leader保持同步的那些Follower都要把消息同步过去,才能认为这条消息是写入成功了。如果说Partition Leader刚接收到了消息,但是结果Follower没有收到消息,此时Leader宕机了,那么客户端会感知到这个消息没发送成功,他会重试再次发送消息过去。此时可能Partition 2的Follower变成Leader了,此时ISR列表里只有最新的这个Follower转变成的Leader了,那么只要这个新的Leader接收消息就算成功了。
- acks=all就代表数据一定不会丢失了吗
当然不是,如果你的Partition只有一个副本,也就是一个Leader,任何Follower都没有,因为ISR里就一个Leader,他接收完消息后宕机,也会导致数据丢失。所以说,这个acks=all,必须跟ISR列表里至少有2个以上的副本配合使用,起码是有一个Leader和一个Follower才可以。 这样才能保证说写一条数据过去,一定是2个以上的副本都收到了才算是成功,此时任何一个副本宕机,不会导致数据丢失。
所以kafka不完全是同步复制和异步复制,通过server,topic,producer的结合配置可以让用户自己权衡数据一致性,可用性。
replica是如何进行恢复的
下面模拟一下整个流程:
-
假设我们一直在写数据,当前leader和各个follower的数据情况如下,
leader(A)
3条数据,follower(B)
同步了2条,follower(C)
同步了1条leader(A) follwer(B) follwer(C) msg1 msg1 msg1 msg2 msg2 msg3 ISR列表为[A,B,C],
leader(A)
commit了msg1
-
leader(A)
挂了,B选举成新的leader,此时ISR列表为[B,C],那么等follower(c)
同步完成msg2
后leader(B)可以提交msg2
,但是无法提交msg3
leader(A)leader(B) follwer(C) msg1msg1 msg1 msg2msg2 msg2 msg3 -
随后数据继续写入
leader(A)leader(B) follwer(C) msg1msg1 msg1 msg2msg2 msg2 msg3msg4 msg4 msg5 msg5 -
后来A启动了,truncate掉
msg1
后面未提交的数据,同步并追赶leader(B)中数据。此时ISR为[A,B,C]leader(A) leader(B) follwer(C) msg1 msg1 msg1 msg2 msg2 msg2 msg4 msg4 msg4 msg5 msg5 msg5
replica全部宕机如何处理
可以选择是否使用ISR中的replica做为重启的leader,选择是与否也会带来不同的区别
-
等待ISR中任一个replica恢复后作为leader
1)等待时间较长,可用性降低 2)如果ISR中所有的replica都无法恢复,则该partition用不可用。
-
选择第一个恢复的replica作为leader,不一定在ISR中(默认配置)
1)可能未包含之前leader提交过的消息,数据丢失 2)可用性高
文章有任何问题欢迎留言,原创文章,码字不易,喜欢可以点赞关注。