Kafka源码阅读 —— KafkaController(3)

来源:互联网 发布:应用文理学院网络学堂 编辑:程序博客网 时间:2024/04/30 18:09

执行 kafka-topics.sh –create

Kafka官网给出的创建Topic的命令如下:
bin/kafka-topics.sh --create --zookeeper localhost:2181 --replication-factor 1 --partitions 1 --topic test
这条命令中的信息包括:zookeeper地址、分区副本数、分区数和topic名。
执行新建Topic的第一步是给各个Partition分配replica:
首先,分配replica有个前提:partition副本数 < broker 数,否则无法做到一个broker上只能有同一partition的一个副本。
分配算法很简单,在代码的注释里有个例子,这里简化了一下:

replica broker-0 broker-1 broker-2 first replica partition1 partition2 partition3 second replica partition3 partition1 partition2 third replica partition2 partition3 partition1

分区1的副本1,副本2,副本3分别位于broker-0,broker-1,broker-2,而分区2则从broker-2开始进行分配,三个副本分别位于broker-1,broker-2,broker-0上,分区3依次类推,位于broker-3,broker-1,broker-2上。
总结下,broker数目为m,副本数为n,则第i个分区从broker编号(i-1)%m 开始分配,i分区的第j个副本分配到编号((i-1)%m+(j-1))%m的broker上。
在实现中,分区1第一个replica分配的broker id是随机的,后面的分配都加上这个随机的偏移量。kafka中,分配返回的结果是 Map[Int, Seq[Int]]类型,对应的是 partitionId -> replicaList,其中replica id和broker id是等价的,因为一个broker上只能有一个副本。replicaList中的顺序是有意义的,partition选举leader时,会参考这个顺序。
分配完replica后,AdminUtils将副本分配信息以json串的方式写入到zookeeper路径/brokers/topics/[topic]/下。到此,命令create topic的工作就做完了,剩下的工作由zookeeper数据变化触发。

状态机响应zookeeper变化

在埋到源码里面之前,先想想,Controller创建Topic,需要执行什么操作呢?

  1. 从zookeeper中读取Topic的信息,在map中保存Partition和所有Replica的状态。Partition状态的含义应该包含是否选举Leader,Leader是否在线等;而Replica的状态也类似,主要是该replica是否在线;
  2. 选举Partition的leader,并将Partition相关信息发送给各个broker;
  3. broker收到Controller发送的Partition信息,创建replica,如果是Leader,需要将ISR,AR,high wartermark等信息保存下来,如果是Follower,则添加到Leader的Fecher,定期从Leader拉取数据。

接下来,看看Kafka是怎么实现这些的呢?
在KafkaController leader选举成功后,在PartitionStateMachine.registerListeners()中注册了路径/brokers/topics/上的监听器TopicChangeListener,在create topic命令修改zookeeper中的数据后会触发函数TopicChangeListener.handleChildChange,它监听topics下子目录列表的变化,执行逻辑如下:

Created with Raphaël 2.1.0开始获取当前topic列表,即/brokers/topics/topics的子目录新topic列表= 当前topic列表 - context中topic列表将新的topic的AR添加到context中通过controller.onNewTopicCreateion创建新topicEnd

KafkaController.onNewTopicCreation函数代码如下:

/* - topics: 新增topic列表 - newPartitions:新增的partition列表*/ def onNewTopicCreation(topics: Set[String], newPartitions: Set[TopicAndPartition]) {    //对每个topic注册/brokers/topics/[topic]/下的监听器    topics.foreach(topic => partitionStateMachine.registerPartitionChangeListener(topic))    //执行新建partition操作    onNewPartitionCreation(newPartitions)  }

上面代码执行了两个操作:

  • 添加监听路径/brokers/topics/[topic]/下数据变化的Listener类AddPartitionsListener,有新的partition加入时函数AddPartitionsListener.handleDataChange被调用,最终通过KafkaController.onNewPartitionCreateaion创建新的partition。
  • 对所有新增topic下的分区调用onNewPartitionCreation。在onNewPartitionCreation中,partition经历了两步状态变化:NonExistentPartition->
    NewPartition
    ->OnlinePartition;同时,partition下的所有replica也一样发生了状态变化:NonExistentReplica->NewReplica->OnlineReplica。Partition的状态变化是通过PartitionStateMachine实现的,而replica的状态变化则是通过ReplicaStateMachine实现的。

那么,在partition和replica状态变化过程中执行了什么操作呢?执行顺序如下:

  • NonExistentPartition-> NewPartition:从zookeeper中读取partition的replica 分配信息到ControllerContext中;
  • NonExistentReplica -> NewReplica:在create topic的场景下,仅更新replica的状态为NewReplica;
  • NewPartition->OnlinePartition:选举Leader,这里的Leader选举方法很简单,直接选择活着的replica列表中的第一个作为Leader,replica分配过程中顺序是有意义的。得到Leader后,向Partition下所有活着的replica发送LeaderAndIsrRequest,replica收到消息后执行的动作参见“Kafka源码阅读 —— KafkaController(2)”;
  • NewReplica->OnlineReplica:在create topic场景下,仅更新replica状态为NewReplica,
    用一个图总结一下create topic执行过程:
    创建topic

Partition状态机

Partition的状态有:NewPartition,OnlinePartition,OfflinePartition和NonExistentPartition这四种。下面是这几个状态之间的转换图:
Partition状态机
大部分的转换都仅仅只更新Partition的状态,其他状态到OnlinePartition,都需要选举Leader,更新zookeeper,并向Partition下的所有replica发送LeaderAndIsrRequest请求;

Replica状态机

Replica的状态的可能值有:NewReplica,OnlineReplica,OfflineReplica,ReplicaDeletionStarted,ReplicaDeletionSuccessful,NonExistentReplica,ReplicaDeletionIneligible。
replica状态机
NewReplica,OnlineReplica,OfflineReplica这三种状态是在正常服务时,replica可能的状态;Replica的状态变化比较复杂一下,详细说下变化到某个状态需要执行的操作:

  • NewReplica:如果在zookeeper中存在分区的LeaderAndIsr信息(该Replica不是在新增topic时新增的),则给该replica发送LeaderAndIsrRequest消息;否则仅更新状态;
  • OnlineReplica:如果是NewReplica->OnlineReplica,则将replica加入到context的相应partition的AR中;如果是从其他状态变更到OnlineReplica,则发送LeaderAndIsrRequest到当前replica;
  • OfflineReplica:首先给当前replica所在broker发送StopReplicaRequest(deletePartition=false)消息;其次,将replica从所在Partition的ISR中移除,变化写入到zookeeper中后给除去当前replica的所有replica发送LeaderAndIsrRequest,更新这些replica所在broker上存放的LeaderAndIsr信息,也别忘了,发送LeaderAndIsrRequest的同时还会发送UpdateMetadataRequest信息;
  • ReplicaDeletionStarted:这个状态表示replica进入删除逻辑,给当前replica所在broker发送StopReplicaRequest(deletePartition=true)消息;“Kafka源码阅读 —— KafkaController(2)”说道过,在这种情形下,broker会删除replica对应的log文件。
  • ReplicaDeletionSuccessful:仅更新状态;
  • NonExistentReplica:删除Replica完成,从context中抹去;
  • ReplicaDeletionIneligible:仅更新状态,这个属于删除异常状态,如删除操作开始后replica所在broker出现failover;
0 0
原创粉丝点击