Kafka 0.8 副本同步机制理解
Kafka的普及在很大程度上归功于它的设计和操作简单,如何自动调优Kafka副本的工作,挑战之一:如何避免follower进入和退出同步副本列表(即ISR)。如果某些topic的部分partition长期处于“under replicated”状态,会增加数据丢失的概率。Kafka通过“多副本机制”实现高可用,当Kafka集群中一个Broker失效情况下仍然保证服务可用。
Kafka日志复制算法保证,如果leader发生故障或挂掉,一个新leader被选举并且客户端的消息成功写入。Kafka确保从同步副本列表中选举一个副本为leader。
0.副本知识
每个Partition有一个预写式日志文件,每个Partition都由一系列有序的、不可变的消息组成,这些消息被连续的追加到Partition中,Partition中的每个消息都有一个连续的序列号叫做offset, 确定它在分区日志中唯一的位置。
- leader处理对这个partition的所有读写请求。
- follower会去复制leader上的数据。
1. in sync 条件
Leader负责跟踪同步副本列表中所有follower滞后状态。
同步中的(in sync),Kafka判断一个节点是否活着有两个条件:
- 节点必须可以维护和ZooKeeper的连接,Zookeeper通过心跳机制检查每个节点的连接。——由参数
request.required.acks
决定,如果是这个参数生效而移除一个follower,说明这个follower 失效或者死亡。 - 如果节点是个follower,他必须能及时的同步leader的写操作,延时不能太久。—— 由参数
replica.lag.max.messages
决定的,如果是这个参数生效而移除一个follower,说明这个follow是一个“慢副本”。
- 一条消息只有被“in sync” list里的所有follower都从leader复制过去才会被认为已提交。这样就避免了部分数据被写进了leader,还没来得及被任何follower复制就宕机了,而造成数据丢失。
- 而对于producer而言,它可以选择是否等待消息commit,这可以通过request.required.acks来设置。
- 对于Consumer而言,只能看见被commit的消息。
1.1问题:
- 被移除后的under replica的follower 会继续拉取leader的数据,等追赶上之后,会被重新加入到“同步副本”。
- 一个消息什么时候被认为是提交的?(意味着可以被consumer消费)
- 直到follower Broker 从同步副本列表中移除
- 或者追赶上leader log end offset,最新的消息才会认为提交。
- 是什么原因导致分区的副本与leader不同步
- 慢副本:在一定周期时间内follower不能追赶上leader。最常见的原因之一是I / O瓶颈导致follower追加复制消息速度慢于从leader拉取速度。
- 卡住副本:在一定周期时间内follower停止从leader拉取请求。follower replica卡住了是由于GC暂停或follower失效或死亡。
- 新启动副本:当用户给主题增加副本因子时,新的follower不在同步副本列表中,直到他们完全赶上了leader日志。
- kafka-0.8 相关集群参数配置
replica.lag.time.max.ms=10000 // 根据队列流量大小和集群负载情况做出判断并设置一个合适值 replica.lag.max.messages=4000
2. Leader 选举
当leader宕机了,怎样在follower中选举出新的leader?
- 实际上,leader election算法非常多,比如Zookeper的Zab, Raft和Viewstamped Replication。而Kafka所使用的leader election算法更像微软的PacificA算法。
一种非常常用的选举leader的方式是“majority vote”(“少数服从多数”),但Kafka并未采用这种方式。这种模式下,如果我们有2f+1个replica(包含leader和follower),那在commit之前必须保证有f+1个replica复制完消息,为了保证正确选出新的leader,fail的replica不能超过f个。---(类似pasox算法)
- 缺点:需要的replica的数量太多,造成性能瓶颈。
leader 选举算法
Kafka在Zookeeper中动态维护了一个ISR(in-sync replicas) set,这个set里的所有replica都跟上了leader,只有ISR里的成员才有被选为leader的可能。在这种模式下,对于f+1个replica,一个Kafka topic能在保证不丢失已经ommit的消息的前提下容忍f个replica的失败。在大多数使用场景中,这种模式是非常有利的。
在ISR中至少有一个follower时,Kafka可以确保已经commit的数据不丢失,但如果某一个partition的所有replica都挂了,就无法保证数据不丢失了。这种情况下有两种可行的方案:
- 等待ISR中的任一个replica“活”过来,并且选它作为leader
- 选择第一个“活”过来的replica(不一定是ISR中的)作为leader
这就需要在可用性和一致性当中作出一个简单的平衡。(Kafka0.8.*使用了第二种方式。)
3.平衡partition
- 默认情况下,kafka以RoundRobin方式写各个partition,让各个partition的消息量均衡。
- 平衡partition的leader在所有的broker上。
优化leadership election的过程也是很重要的,毕竟这段时间相应的partition处于不可用状态。
一种简单的实现是暂停宕机的broker上的所有partition,并为之选举leader。实际上,Kafka选举一个broker作为controller,这个controller通过watch Zookeeper检测所有的broker failure,并负责为所有受影响的parition选举leader,再将相应的leader调整命令发送至受影响的broker,过程如下图所示。
4.Controller
负责leader 选举,每个broker都可成为Controller。
它可以批量的通知leadership的变化,从而使得选举过程成本更低。如果controller失败了,所有broker都会尝试在Zookeeper中创建/controller->{this broker id},如果创建成功(只可能有一个创建成功),则该broker会成为controller。
Controller对Broker failure的处理过程
- Controller在Zookeeper的/brokers/ids节点上注册Watch。一旦有Broker宕机(本文用宕机代表任何让Kafka认为其Broker die的情景,包括但不限于机器断电,网络不可用,GC导致的Stop The World,进程crash等),其在Zookeeper对应的Znode会自动被删除,Zookeeper会fire Controller注册的Watch,Controller即可获取最新的幸存的Broker列表。
- Controller决定set_p,该集合包含了宕机的所有Broker上的所有Partition。
- 对set_p中的每一个Partition:
3.1 从/brokers/topics/[topic]/partitions/[partition]/state读取该Partition当前的ISR。
3.2 决定该Partition的新Leader。如果当前ISR中有至少一个Replica还幸存,则选择其中一个作为新Leader,新的ISR则包含当前ISR中所有幸存的Replica。否则选择该Partition中任意一个幸存的Replica作为新的Leader以及ISR(该场景下可能会有潜在的数据丢失)。如果该Partition的所有Replica都宕机了,则将新的Leader设置为-1。
3.3 将新的Leader,ISR和新的leader_epoch及controller_epoch写入/brokers/topics/[topic]/partitions/[partition]/state。注意,该操作只有Controller版本在3.1至3.3的过程中无变化时才会执行,否则跳转到3.1。
- 直接通过RPC向set_p相关的Broker发送LeaderAndISRRequest命令。Controller可以在一个RPC操作中发送多个命令从而提高效率.
Broker failover顺序图如下所示。
5. 消息保障
kafka能够保障以下两点:
- At most once 消息可能会丢,但绝不会重复传输
- At least once 消息绝不会丢,但可能会重复传输
对于Producer
- 发送不管,at most once
- 发送管ack,at least once
对于Consumer
- 记录Offset,at least once。
posted on 2017-02-06 09:44 BYRHuangQiang 阅读(2174) 评论(0) 编辑 收藏 举报