概念
- 创建Topic的时候可以指定 --replication-factor 3 ,表示分区的副本数,不要超过broker的数量。
- Leader是负责读写的节点,⽽其他副本则是Follower。Producer只把消息发送到Leader,Follower定期地到Leader上Pull数据。
- ISR是Leader负责维护的与其保持同步的Replica列表,即当前活跃的副本列表。如果⼀个Follow落后太多,Leader会将它从ISR中移除。落后太多意思是该Follow复制的消息落后于Leader的条数超过预定值(参数: replica.lag.max.messages 默认值:4000)或者Follow⻓时间没有向Leader发送fetch请求(参数: replica.lag.time.max.ms 默认值:10000)。
- 为了保证可靠性,可以设置 acks=all 。Follower收到消息后,会像Leader发送ACK。⼀旦Leader收到了ISR中所有Replica的ACK,Leader就commit,那么Leader就向Producer发送ACK。
副本的分配:
当某个topic的 --replication-factor 为N(N>1)时,每个Partition都有N个副本,称作replica。原则上是将replica均匀的分配到整个集群上。不仅如此,partition的分配也同样需要均匀分配,为了更好的负载均衡。
副本分配的三个⽬标:
- 均衡地将副本分散于各个broker上
- 对于某个broker上分配的分区,它的其他副本在其他broker上
- 如果所有的broker都有机架信息,尽量将分区的各个副本分配到不同机架上的broker。
在不考虑机架信息的情况下: - 第⼀个副本分区通过轮询的⽅式挑选⼀个broker,进⾏分配。该轮询从broker列表的随机位置进⾏轮询。
-
其余副本通过增加偏移进⾏分配。
Leader的选举
如果Leader宕机了该怎么办?很容易想到我们在Follower中重新选举⼀个Leader,但是选举哪个作为leader呢?
Follower可能已经落后许多了,因此我们要选择的是”最新”的Follow:新的Leader必须拥有与原来Leader commit过的所有信息。
kafka动态维护⼀组同步leader数据的副本(ISR),只有这个组的成员才有资格当选leader,kafka副本写⼊不被认为是已提交,直到所有的同步副本已经接收才认为。这组ISR保存在zookeeper,正因为如此,在ISR中的任何副本都有资格当选leader。
基于Zookeeper的选举⽅式
⼤数据很多组件都有Leader选举的概念,如HBASE等。它们⼤都基于ZK进⾏选举,所有Follow都在ZK上⾯注册⼀个Watch,⼀旦Leader宕机,Leader对于的Znode会⾃动删除,那些Follow由于在Leader节点上注册了Watcher,故可以得到通知,就去参与下⼀轮选举,尝试去创建该节点,zK会保证只有⼀个Follow创建成功,成为新的Leader。
但是这种⽅式有⼏个缺点:
split-brain。这是由ZooKeeper的特性引起的,虽然ZooKeeper能保证所有Watch按顺序触发,但并不能保证同⼀时刻所有Replica“看”到的状态是⼀样的,这就可能造成不同Replica的响应不⼀致。
herd effect。如果宕机的那个Broker上的Partition⽐较多,会造成多个Watch被触发,造成集群内⼤量的调整。
ZooKeeper负载过重。每个Replica都要为此在ZooKeeper上注册⼀个Watch,当集群规模增加到⼏千个Partition时ZooKeeper负载会过重。
基于Controller的选举⽅式
Kafka 0.8后的Leader Election⽅案解决了上述问题,它在所有broker中选出⼀个controller,所有Partition的Leader选举都由controller决定。controller会将Leader的改变直接通过RPC的⽅式(⽐ZooKeeper Queue的⽅式更⾼效)通知需为为此作为响应的Broker。同时controller也负责增删Topic以及Replica的重新分配。
优点:极⼤缓解了Herd Effect问题、减轻了ZK的负载,Controller与Leader/Follower之间通过RPC通信,⾼效且实时。
缺点:引⼊Controller增加了复杂度,且需要考虑Controller的Failover。
如何处理Replica的恢复
1:只有当ISR列表中所有列表都确认接收数据后,该消息才会被commit。因此只有m1被commit了。即使leader上有m1,m2,m3,consumer此时只能读到m1。
2:此时A宕机了。B变成了新的leader了,A从ISR列表中移除。B有m2,B会发给C,C收到m2后,m2被commit。
3:B继续commit消息4和5
4:A回来了。注意A并不能⻢上在isr列表中存在,因为它落后了很多,只有当它接受了⼀些数据,⽐如m2,m4,m5,它不落后太多的时候,才会回到ISR列表中。
思考:m3怎么办呢?
两种情况:
- A重试,重试成功了,m3就恢复了,但是乱序了。
- A重试不成功,此时数据就可能丢失了。
如果Replica都死了怎么办?
只要⾄少有⼀个replica,就能保证数据不丢失,可是如果某个partition的所有replica都死了怎么办?有两种⽅案:
- 等待在ISR中的副本恢复,并选择该副本作为Leader。
- 选择第⼀个活过来的副本(不⼀定在 ISR中),作为Leader。
可⽤性和⼀致性的⽭盾:如果⼀定要等待副本恢复,等待的时间可能⽐较⻓,甚⾄可能永远不可⽤。如果是第⼆种,不能保证所有已经commit的消息不丢失,但有可⽤性。
Kafka默认选⽤第⼆种⽅式,⽀持选择不能保证⼀致的副本。
可以通过参数 unclean.leader.election.enable 禁⽤它。
Broker宕机怎么办?
Controller在Zookeeper的/brokers/ids节点上注册Watch。⼀旦有Broker宕机,其在Zookeeper对应的Znode会⾃动被删除,Zookeeper会fire Controller注册的Watch,Controller即可获取最新的幸存的Broker列表。
Controller决定set_p,该集合包含了宕机的所有Broker上的所有Partition。 对set_p中的每⼀个Partition:
- 从/brokers/topics/[topic]/partitions/[partition]/state读取该Partition当前的ISR。
- 决定该Partition的新Leader。如果当前ISR中有⾄少⼀个Replica还幸存,则选择其中⼀个作为新Leader,新的ISR则包含当前ISR中所有幸存的Replica。否则选择 该Partition中任意⼀个幸存的Replica作为新的Leader以及ISR(该场景下可能会有潜在的数据丢失)。如果该Partition的所有Replica都宕机了,则将新的Leader设置为-1。
- 将新的Leader,ISR和新的leader_epoch及controller_epoch写⼊/brokers/topics/[topic]/partitions/[partition]/state。
[zk: localhost:2181(CONNECTED) 13] get
/brokers/topics/bdstar/partitions/0/state{"controller_epoch":1272,"leader":0,"v
ersion":1,"leader_epoch":4,"isr":[0,2]}
直接通过RPC向set_p相关的Broker发送LeaderAndISRRequest命令。Controller可以在⼀个RPC操作中发送多个命令从⽽提⾼效率。
Controller宕机怎么办?
每个Broker都会在/controller上注册⼀个Watch。
[zk: localhost:2181(CONNECTED) 19] get /controller
{"version":1,"brokerid":1...............}
当前Controller宕机时,对应的/controller会⾃动消失。所有“活”着的Broker竞选成为新的Controller,会创建新的Controller Path
[zk: localhost:2181(CONNECTED) 19] get /controller
{"version":1,"brokerid":2...............}
注意:只会有⼀个竞选成功(这点由Zookeeper保证)。竞选成功者即为新的Leader,竞选失败者则重新在新的Controller Path上注册Watch。因为Zookeeper的Watch是⼀次性的,被fire⼀次之后即失效,所以需要重新注册
失效副本
Kafka中,⼀个主题可以有多个分区,增强主题的可扩展性,为了保证靠可⽤,可以为每个分区设置副本数。
只有Leader副本可以对外提供读写服务,Follower副本只负责poll Leader副本的数据,与Leader副本保持数据的同步。
系统维护⼀个ISR副本集合,即所有与Leader副本保持同步的副本列表。
当Leader宕机找不到的时候,就从ISR列表中挑选⼀个分区做Leader。
如果ISR列表中的副本都找不到了,就剩下OSR的副本了。
此时,有两个选择:要么选择OSR的副本做Leader,优点是可以⽴即恢复该分区的服务。缺点是可能会丢失数据。
要么选择等待,等待ISR列表中的分区副本可⽤,就选择该可⽤ISR分区副本做Leader。优点是不会丢失数据缺点是会影响当前分区的可⽤性。因为