Kafka是一个高性能,高容错,多副本,可复制的分布式消息系统,是基于Raft算法来实现leader选举的。整个架构设计中涉及几处选举:

  • 控制器(Broker)选举
  • 分区多副本选举
  • 消费组选举

1 控制器(Broker)选举

        所谓控制器就是一个Borker,在一个kafka集群中,有多个broker节点,但是它们之间需要选举出一个leader,其他的broker充当follower角色。

  • 集群中第一个启动的broker会通过在zookeeper中创建临时节点/controller来让自己成为控制器,其他broker启动时也会在zookeeper中创建临时节点,但是发现节点已经存在,所以它们会收到一个异常,意识到控制器已经存在,那么就会在zookeeper中创建watch对象,便于它们收到控制器变更的通知。
  • 如果控制器由于网络原因与zookeeper断开连接或者异常退出,那么其他broker通过watch收到控制器变更的通知,就会去尝试创建临时节点/controller,如果有一个broker创建成功,那么其他broker就会收到创建异常通知,也就意味着集群中已经有了控制器,其他broker只需创建watch对象即可。
  • 如果集群中有一个broker发生异常退出了,那么控制器就会检查这个broker是否有分区的副本leader,如果有那么这个分区就需要一个新的leader,此时控制器就会去遍历其他副本,决定哪一个成为新的leader,同时更新分区的ISR集合。
  • 如果有一个broker加入集群中,那么控制器就会通过Broker ID去判断新加入的broker中是否含有现有分区的副本,如果有,就会从分区副本中去同步数据。

        集群中每选举一次控制器,就会通过zookeeper创建一个controller epoch,每一个选举都会创建一个更大,包含最新信息的epoch,如果有broker收到比这个epoch旧的数据,就会忽略它们,kafka也通过这个epoch来防止集群产生“脑裂”。

2 分区副本选举机制

        在kafka的集群中,会存在着多个主题topic,在每一个topic中,又被划分为多个partition,为了防止数据不丢失,每一个partition又有多个副本,在整个集群中,总共有三种角色:

  • leader:每个分区都有一个leader,为了保证数据一致性,所有的生产者与消费者的请求都会经过该副本来处理。
  • follower:除了leader外的其他所有副本都是跟follower,follower不处理来自客户端的任何请求,只负责从leader同步数据,保证与follower保持一致。如果follower发生崩溃,就会从follower中选举出一个leader。
  • preferred leader:如果一个分区有3个副本,且这3个副本的优先级别分别为0,1,2,根据优先副本的概念,0会作为leader 。当0节点的broker挂掉时,会启动1这个节点broker当做leader。当0节点的broker再次启动后,会自动恢复为此partition的leader。不会导致负载不均衡和资源浪费,这就是leader的均衡机制。

3 消费组选主

        在kafka的消费端,会有一个消费者协调器以及消费组,组协调器GroupCoordinator需要为消费组内的消费者选举出一个消费组的leader,那么如何选举的呢?

  • 如果消费组内还没有leader,那么第一个加入消费组的消费者即为消费组的leader
  • 如果某一个时刻leader消费者由于某些原因退出了消费组,那么就会重新选举leader,如何选举?
private val members = new mutable.HashMap[String, MemberMetadata]
 leaderId = members.keys.headOption// member是一个hashmap的数据结构,key为消费者的member_id,value是元数据信息,
// 那么它会将leaderId选举为Hashmap中的第一个键值对,它和随机基本没啥区别。