1. 首页
  2. 大数据
  3. Kafka教程

【Kafka教程】(六)Kafka High Available高可用架构

假设没有高可用架构,一旦某个broker宕机,则宕机期间其上的所有partition都无法提供服务,甚至导致数据丢失。因此必须有HA架构。
回顾之前的教程,我们已经了解到replication、leader、ISR、ACK的概念:

  • 我们创建topic的时候可以指定–replication-factor 3,表示分区的副本数(一般根据broker数量决定)
  • 我们查看topic信息的时候,会发现有leader和isr的概念
  • 我们说过leader是负责读写的节点,为了保证较高的处理效率,消息的读写都是在固定的一个副本上完成。这个副本就是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)
  • 为了保证可靠性,我们通常设置ACK=all。Follower收到消息后,会像Leader发送ACK。一旦Leader收到了ISR中所有Replica的ACK,leader就commit,那么Leader就像Producer发送ACK。

这些概念是HA的基础。

副本的分配

当某个topic的–replication-factor为N(N>1)时,每个Partition都有N个副本,称作replica。假设,我所有的replica都在同一台机器上,有用吗?那台机器挂了就没用了。因此副本必须是有一定的分配算法,原则上是将replica均匀的分配到整个集群上。不仅如此,partition的分配也同样需要均匀分配,为了更好的负载均衡。
【Kafka教程】(六)Kafka High Available高可用架构
KAFKA的分配算法如下:

  • 将所有N Broker和待分配的i个Partition排序.
  • 将第i个Partition分配到第(i mod n)个Broker上.
  • 将第i个Partition的第j个副本分配到第((i + j) mod n)个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的恢复

【Kafka教程】(六)Kafka High Available高可用架构

  • 从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。

这就需要在可用性和一致性当中做个折衷。如果一定要等待副本起死回生,那么等待的时间可能比较长,而且如果都没办法活过来,那么pertition将永远不可用,这样降低了可用性。如果是第二种,可能不能保证包含所有已经commit的消息,因此会造成数据丢失,但保证了可用性。目前KAFKA选用的是第二种方式,支持选择不能保证一致的副本。你也可以通过参数unclean.leader.election.enable禁用它。

Broker宕机怎么办?

  • 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:
      1、从/brokers/topics/[topic]/partitions/[partition]/state读取该Partition当前的ISR。
      2、决定该Partition的新Leader。如果当前ISR中有至少一个Replica还幸存,则选择其中一个作为新Leader,新的ISR则包含当前ISR中所有幸存的Replica。否则选择该Partition中任意一个幸存的Replica作为新的Leader以及ISR(该场景下可能会有潜在的数据丢失)。如果该Partition的所有Replica都宕机了,则将新的Leader设置为-1。
      3、将新的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,"version":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宕机时(此处我kill了id=1的broker进程),对应的/controller会自动消失。所有“活”着的Broker都会去竞选成为新的Controller,会创建新的Controller Path

[zk: localhost:2181(CONNECTED) 19] get /controller
{"version":1,"brokerid":2...............}

注意:只会有一个竞选成功(这点由Zookeeper保证)。竞选成功者即为新的Leader,竞选失败者则重新在新的Controller Path上注册Watch。因为Zookeeper的Watch是一次性的,被fire一次之后即失效,所以需要重新注册。

BDStar原创文章。发布者:Liuyanling,转载请注明出处:http://bigdata-star.com/archives/1510

发表评论

登录后才能评论

联系我们

562373081

在线咨询:点击这里给我发消息

邮件:562373081@qq.com

工作时间:周一至周五,9:30-18:30,节假日休息

QR code