1.高可用概述
高可⽤性(High Availability),指零碎无间断地执⾏其性能的能力,代表零碎的可⽤性水平。Kafka从0.8版本开始提供了高可⽤机制, 可保障⼀个或多个Broker宕机后,其余Broker及所有Partition都能持续提供服务,且存储的音讯不失落。
对分布式系来说,当集群规模回升到肯定水平后,一台或者多台机器宕机的可能性⼤大减少;Kafka采⽤多机备份和音讯应答确认形式解决了数据失落问题,并通过一套失败复原机制解决服务不可⽤问题。
2.音讯备份机制
2.1 音讯备份
Kafka容许同⼀个Partition存在多个音讯正本(Replica),每个Partition的正本通常由1个Leader及0个以上的Follower组成,⽣产者将 音讯间接发往对应Partition的Leader,Follower会周期地向Leader发送同步申请,Kafka的Leader机制在保障数据⼀致性地同时升高了了 音讯备份的复杂度。
同⼀Partition的Replica不应存储在同一个Broker上,因为一旦该Broker宕机,对应Partition的所有Replica都无奈⼯作,这就达不到 高可用的成果。为了做好负载平衡并提⾼容错能力,Kafka会尽量将所有的Partition以及各Partition的正本平均地调配到整个集群上。 举个例⼦,当集群中部署了3台Broker,TopicA共有4个Partition,每个Partition均有3个Replica时下图就是⼀种正当理的散布形式。
2.2 ISR
ISR(In-Sync Replicas)指的是⼀个Partition中与Leader“放弃同步”的Replica列表(理论存储的是正本所在Broker的BrokerId),这里的 放弃同步不是指与Leader数据放弃齐全⼀统一,只需在replica.lag.time.max.ms工夫内与Leader放弃无效连贯,官⽅方解释如下
If a follower hasn't sent any fetch requests or hasn't consumed up to the leaders log end offset for at least this time, the leader will remove the follower from isr,( default value =10000 )
Follower周期性地向Leader发送FetchRequest申请(数据结构⻅见下),发送工夫距离配置在replica.fetch.wait.max.ms中,默认值为 500。
public class FetchRequest { private final short versionId; private final int correlationId; private final String clientId; private final int replicaId; private final int maxWait; // Follower容忍的最⼤大等待时间: 到点Leader⽴立刻返回后果,默认值500 private final int minBytes; // Follower容忍的最⼩小返回数据⼤大⼩小:当Leader有⾜足够数据时⽴立刻返回,兜底期待ma private final Map<TopicAndPartition, PartitionFetchInfo> requestInfo; // Follower中各Partititon}
各Partition的Leader负责保护ISR列表并将ISR的变更同步至ZooKeeper,被移出ISR的Follower会持续向Leader发FetchRequest请 求,试图再次跟上Leader从新进⼊入ISR。
ISR中所有正本都跟上了Leader,通常只有ISR里的成员才可能被选为Leader。当Kafka中unclean.leader.election.enable配置为 true(默认值为false)且ISR中所有正本均宕机的状况下,才容许ISR外的正本被选为Leader,此时会失落局部已应答的数据。
2.3 Acks
为了了讲清楚ISR的作⽤用,下⾯面介绍⼀下⽣产者能够抉择的音讯应答⽅式,⽣产者发送音讯中蕴含acks字段,该字段代表Leader应答⽣产者前Leader收到的应答数
- acks = 0
⽣产者⽆需期待服务端的任何确认,音讯被增加到生产者套接字缓冲区后就视为已发送,因而acks=0不不能保障服务端已收到音讯, 使⽤用场景较少,本⽂文不不做任何探讨
- acks = 1
Leader将音讯写⼊入本地⽇日志后⽆无需期待Follower的音讯确认就做出应答。如果Leader在应答音讯后⽴立刻宕机且其余Follower均未完 成音讯的复制,则该条音讯将失落
上图左侧的稳态场景下,Partition1的数据冗余备份在Broker0和Broker2上;Broker0中的正本与Leader正本因⽹网络开销等因素存在1 秒钟同步时间差,Broker0中的正本落后124条音讯;Broker2中的正本存在8秒钟同步时间差,Broker2中的正本落后7224条音讯。若 图中的Broker1忽然宕机且Broker0被选为Partition1的Leader,则在Leader宕机前写入的124条音讯未同步⾄至Broker0中的正本,这次宕 机会造成大量量音讯失落。
- acks = all
Leader将期待ISR中的所有正本确认后再做出应答,因而只有ISR中任何⼀一个正本还存活着,这条应答过的音讯就不会失落。 acks=all是可⽤用性最⾼的抉择,但期待Follower应答引入了额定的响应工夫。Leader须要期待ISR中所有正本做出应答,此时响应工夫 取决于ISR中最慢的那台机器,下图中因复制产生的额定提早为3秒。
Broker的配置项min.insync.replicas(默认值为1)代表了失常写⼊⽣产者数据所须要的起码ISR个数,当ISR中的正本数量⼩于 min.insync.replicas时,Leader停⽌止写⼊⽣产者⽣产的音讯,并向⽣产者抛出NotEnoughReplicas异样,阻塞期待更更多的Follower赶上 并从新进⼊ISR。被Leader应答的音讯都至多有min.insync.replicas个正本,因而可能容忍min.insync.replicas-1个正本同时宕机。 小结:发送的acks=1音讯会呈现失落状况,为不失落音讯可配置⽣产者acks=all & min.insync.replicas >= 2
2.4 LEO & HW
每个Kafka正本对象都有上面两个重要属性:
- LEO(log end offset) ,即⽇志末端偏移,指向了正本日志中下⼀条音讯的位移值(即下一条音讯的写⼊地位)
- HW(high watermark),即已同步音讯标识,因其相似于⽊桶效应中短板决定⽔位高度,故取名高⽔位线
所有⾼⽔位线以下音讯都是已备份过的,消费者仅可生产各分区Leader⾼水位线以下的音讯,对于任何⼀个正本对象而⾔其HW值不会大于LEO值
Leader的HW值由ISR中的所有备份的LEO最小值决定(Follower在发送FetchRequest时会在PartitionFetchInfo中会携带Follower的LEO)
Kafka本来使⽤用HW来记录正本的备份进度,HW值的更新通常须要额定一轮FetchRequest能力实现,存在一些边缘案例导致备份数据失落或导致多个备份间的数据不统一。本⽂次要介绍可用性,数据⼀一致性及截断规定不详述。Kafka新引入了Leader epoch解决HW 截断产⽣的问题,有趣味的同学可参考 Apache : Fix log divergence after fast leader fail over
3.故障复原
3.1 Broker故障复原
Kafka从0.8版本开始引⼊了一套Leader选举及失败复原机制:⾸先须要在集群所有Broker中选出⼀个Controller,负责各Partition的 Leader选举以及Replica的重新分配。当呈现Leader故障后,Controller会将Leader/Follower的变动告诉到需为此作出响应的Broker。
Kafka使⽤ZooKeeper存储Broker、Topic等状态数据,Kafka集群中的Controller和Broker会在ZooKeeper指定节点上注册 Watcher(事件监听器器),以便在特定事件触发时,由ZooKeeper将事件告诉到对应Broker。
3.1.1 Broker故障场景剖析
- 场景1 Broker与其余Broker断开连接
上图中Broker0和其余Broker都断开了连贯,因为ZooKeeper还能接管到Broker0的⼼跳,因而ZooKeeper认为Broker0仍然存活,则对于
Partition0
Broker0中的正本为Partition0的Leader,当Broker0超过replica.lag.time.max.ms没接管到Broker1、Broker2的FetchRequest申请后, Broker0抉择将Partition0的ISR膨胀到仅剩Broker0自身,并将ISR的变更更同步到ZooKeeper;Broker0须要依据min.insync.replicas的配置决定是否持续承受生产者数据
Partition1
超过replica.lag.time.max.ms后,Broker1会将Broker0中的正本从Partition1的ISR中移除。若后续Broker0复原连贯并赶上了了Broker1, 则Broker1还会再将Broker0从新加⼊入Partition1的ISR
- 场景2 Broker与ZooKeeper断开连接
Broker0与ZooKeeper断开连接后,ZooKeeper会⾃主动删除该Broker对应节点,并且认为Broker0曾经宕机,则对于
Partition0
ZooKeeper删除节点后,该节点上注册的Watcher会告诉Controller,Controller会发现Broker0为Partition0的Leader,于是从以后 存活的ISR中抉择了了Broker2作为Partition0的新Leader。Controller通过LeaderAndIsrRequest将Leader变更更告诉到Broker1、Broker2, 于是Broker1改向Broker2发送Partition0数据的FetchRequest申请。
⽣生产者每隔60秒会从bootstrap.servers中的Broker获取最新的metadata,当发现Partition0的Leader发⽣生变更更后,会改向新 Leader-Broker2发送Partition0数据。另⼀一边,Broker0收不到ZooKeeper告诉,仍然认为⾃自⼰己是Partition0的Leader;因为Broker1、 Broker2不不再向Broker0发送FetchRequest申请,缺失了了ISR应答的Broker0停⽌止写⼊入acks=all的音讯,但能够持续写⼊入acks=1的音讯。 在replica.lag.time.max.ms工夫后,Broker0尝试向ZooKeeper发送ISR变更更申请但失败了了,于是不不再接管⽣生产者的音讯。
当Broker0与ZooKeeper复原连贯后,发现⾃自⼰己不不再是Partition0的Leader,于是将本地⽇日志截断(为了了保障和Leader数据⼀一致性), 并开始向Broker2发送FetchRequest申请。在Broker0与ZooKeeper失联期间写⼊入Broker0的所有音讯因为未在新Leader中备份,这些 音讯都失落了了。
Partition1
Broker0中的正本只是作为Partition1的Follower节点,⽽而Broker0与Broker1仍然放弃连贯,因而Broker0仍然会向Broker1发送 FetchRequest。只有Broker0能持续放弃同步,Broker1也不不会向ZooKeeper变更更ISR。
3.1.2 Broker故障复原过程
Broker发⽣生故障后,由Controller负责选举受影响Partition的新Leader并告诉到相干Broker,具体过程可参考下图。
当Broker呈现故障与ZooKeeper断开连接后,该Broker在ZooKeeper对应的znode会⾃主动被删除,ZooKeeper会触发Controller注册 在该节点的Watcher;Controller从ZooKeeper的/brokers/ids节点上获取宕机Broker上的所有Partition(简称set_p);Controller再从 ZooKeeper的/brokers/topics获取set_p中所有Partition以后的ISR;对于宕机Broker是Leader的Partition,Controller从ISR中抉择幸存 的Broker作为新Leader;最初Controller通过LeaderAndIsrRequest申请向set_p中的Broker发送LeaderAndISRRequest申请。
受到影响的Broker会收到Controller发送的LeaderAndIsrRequest申请后,Broker通过ReplicaManager的becomeLeaderOrFollower ⽅办法响应LeaderAndIsrRequest:新Leader会将HW更更新为它的LEO值,⽽而Follower则通过⼀一系列列策略略截断log以保证数据⼀一致性。
3.2 Controller故障复原
3.2.1 Controller 故障场景剖析
- 场景1 Controller与ZooKeeper断开连接
此时ZooKeeper会将Controller长期节点删除,并依照下节的故障复原过程从新竞选出新Controller。⽽而本来的Controller因为⽆无奈连 上ZooKeeper,它什什么也执⾏行行不不了了;当它与ZooKeeper复原连贯后发现⾃自⼰己不不再是Controller,会在Kafka集群中充当⼀一个一般的 Broker。
- 场景2 Controller与某个Broker断开连接
因为Controller⽆无奈告诉到Broker0,所以Broker0不不知道Partition0的Leader曾经更更换了了,所以也会呈现3.1.1节场景2形容的呈现短暂 服务不不可⽤用并可能发⽣生数据失落。
3.2.2 Controller 故障复原过程
最初,集群中的Controller也会呈现故障,因而Kafka让所有Broker都在ZooKeeper的Controller节点上注册⼀一个Watcher。Controller
发⽣生故障时对应的Controller长期节点会⾃主动删除,此时注册在其上的Watcher会被触发,所有活着的Broker都会去竞选成为新的 Controller(即创立新的Controller节点,由ZooKeeper保障只会有⼀一个创立胜利)。竞选成功者即为新的Controller,会在ZooKeeper的 下述节点上注册Watcher,以监控各Broker运⾏行行状态、负责Leader宕机的失败复原,并对治理理脚本做出响应。
- 在/admin节点上注册Watcher,以应答治理理员脚本对Topic及Partition的影响;
- 在/brokers/ids节点上注册Watcher,以获取各Brokers的状态变动;
- 在/brokers/topics节点上注册Watcher,以监控每个Partition的ISR正本状态;