跟我学Kafka之Controller控制器详解

作者:小程

我们的kafka源码分享已经进行过很多期了,主要的内容也都分享的差不多了,那么在今后的分享中,主要集中在kafka性能优化和使用。

Kafka集群中的其中一个Broker会被选举为Controller,主要负责Partition管理和副本状态管理,也会执行类似于重分配Partition之类的管理任务。如果当前的Controller失败,会从其他正常的Broker中重新选举Controller。

进入KafkaController.scala文件看到如下代码:

class KafkaController(val config : KafkaConfig, zkClient: ZkClient, val brokerState: BrokerState) extends Logging with KafkaMetricsGroup {
  this.logIdent = "[Controller " + config.brokerId + "]: "
  private var isRunning = true
  private val stateChangeLogger = KafkaController.stateChangeLogger
  val controllerContext = new ControllerContext(zkClient, config.zkSessionTimeoutMs)
  val partitionStateMachine = new PartitionStateMachine(this)
  val replicaStateMachine = new ReplicaStateMachine(this)
  private val controllerElector = new ZookeeperLeaderElector(controllerContext, ZkUtils.ControllerPath, onControllerFailover,
    onControllerResignation, config.brokerId)
  // have a separate scheduler for the controller to be able to start and stop independently of the
  // kafka server
  private val autoRebalanceScheduler = new KafkaScheduler(1)
  var deleteTopicManager: TopicDeletionManager = null
  val offlinePartitionSelector = new OfflinePartitionLeaderSelector(controllerContext, config)
  private val reassignedPartitionLeaderSelector = new ReassignedPartitionLeaderSelector(controllerContext)
  private val preferredReplicaPartitionLeaderSelector = new PreferredReplicaPartitionLeaderSelector(controllerContext)
  private val controlledShutdownPartitionLeaderSelector = new ControlledShutdownLeaderSelector(controllerContext)
  private val brokerRequestBatch = new ControllerBrokerRequestBatch(this)

  private val partitionReassignedListener = new PartitionsReassignedListener(this)
  private val preferredReplicaElectionListener = new PreferredReplicaElectionListener(this

在KafkaController类中定义了很多属性,我们先重点了解下面的PartitionLeaderSelector对象,主要是为分区选举出leader broker,该trait只定义了一个方法selectLeader,接收一个TopicAndPartition对象和一个LeaderAndIsr对象。TopicAndPartition表示要选leader的分区,而第二个参数表示zookeeper中保存的该分区的当前leader和ISR记录。该方法会返回一个元组包括了推举出来的leader和ISR以及需要接收LeaderAndISr请求的一组副本。

trait PartitionLeaderSelector {
    def selectLeader(topicAndPartition: TopicAndPartition, currentLeaderAndIsr: LeaderAndIsr): (LeaderAndIsr, Seq[Int])
}

通过我们上面的代码,可以看到在KafkaController中共定义了五种selector选举器:

  • 1、NoOpLeaderSelector
  • 2、OfflinePartitionLeaderSelector
  • 3、ReassignedPartitionLeaderSelector
  • 4、PreferredReplicaPartitionLeaderSelector
  • 5、ControlledShutdownLeaderSelector

我们在解释这五个选择器之前,先了解一下在Kafka中Partition的四种状态:

  • NonExistentPartition —— 这个状态表示该分区要么没有被创建过或曾经被创建过但后面被删除了。
  • NewPartition —— 分区创建之后就处于NewPartition状态。在这个状态中,分区应该已经分配了副本,但是还没有选举出leader和ISR。
  • OnlinePartition —— 一旦分区的leader被推选出来,它就处于OnlinePartition状态。
  • OfflinePartition —— 如果leader选举出来后,leader broker宕机了,那么该分区就处于OfflinePartition状态。

四种状态的转换关系如下:

NonExistentPartition -> NewPartition

  1. 首先将第一个可用的副本broker作为leader broker并把所有可用的副本对象都装入ISR,然后写leader和ISR信息到zookeeper中保存
  2. 对于这个分区而言,发送LeaderAndIsr请求到每个可用的副本broker,以及UpdateMetadata请求到每个可用的broker上

OnlinePartition, OfflinePartition -> OnlinePartition

为该分区选取新的leader和ISR以及接收LeaderAndIsr请求的一组副本,然后写入leader和ISR信息到zookeeper中保存。

NewPartition, OnlinePartition -> OfflinePartition

标记分区状态为离线(offline)。

OfflinePartition -> NonExistentPartition

离线状态标记为不存在分区,表示该分区失败或者被删除。

在介绍完最基本的概念之后,下面我们将重点介绍上面提到过的五种选举器:
1、ReassignedPartitionLeaderSelector
从可用的ISR中选取第一个作为leader,把当前的ISR作为新的ISR,将重分配的副本集合作为接收LeaderAndIsr请求的副本集合。
2、PreferredReplicaPartitionLeaderSelector
如果从assignedReplicas取出的第一个副本就是分区leader的话,则抛出异常,否则将第一个副本设置为分区leader。
3、ControlledShutdownLeaderSelector
将ISR中处于关闭状态的副本从集合中去除掉,返回一个新新的ISR集合,然后选取第一个副本作为leader,然后令当前AR作为接收LeaderAndIsr请求的副本。
4、NoOpLeaderSelector
原则上不做任何事情,返回当前的leader和isr。
5、OfflinePartitionLeaderSelector
从活着的ISR中选择一个broker作为leader,如果ISR中没有活着的副本,则从assignedReplicas中选择一个副本作为leader,leader选举成功后注册到Zookeeper中,并更新所有的缓存。

所有的leader选择完成后,都要通过请求把具体的request路由到对应的handler处理。目前kafka并没有把handler抽象出来,而是每个handler都是一个函数,混在KafkaApi类中。
其实也就是如下的代码:

def handle(request: RequestChannel.Request) {
  try{
    trace("Handling request: " + request.requestObj + " from client: " + request.remoteAddress)
    request.requestId match {
      case RequestKeys.ProduceKey => handleProducerRequest(request)  // producer
      case RequestKeys.FetchKey => handleFetchRequest(request)       // consumer
      case RequestKeys.OffsetsKey => handleOffsetRequest(request)
      case RequestKeys.MetadataKey => handleTopicMetadataRequest(request)
      case RequestKeys.LeaderAndIsrKey => handleLeaderAndIsrRequest(request) //成为leader或follower设置同步副本组信息
      case RequestKeys.StopReplicaKey => handleStopReplicaRequest(request)
      case RequestKeys.UpdateMetadataKey => handleUpdateMetadataRequest(request)
      case RequestKeys.ControlledShutdownKey => handleControlledShutdownRequest(request)  //shutdown broker
      case RequestKeys.OffsetCommitKey => handleOffsetCommitRequest(request)
      case RequestKeys.OffsetFetchKey => handleOffsetFetchRequest(request)
      case requestId => throw new KafkaException("Unknown api code " + requestId)
    }
  } catch {
    case e: Throwable =>
      request.requestObj.handleError(e, requestChannel, request)
      error("error when handling request %s".format(request.requestObj), e)
  } finally
    request.apiLocalCompleteTimeMs = SystemTime.milliseconds
}

这里面的每个请求在上面给出的链接的文章中都有过解释说明,在这里不多解释。

RequestKeys.LeaderAndIsr详细分析
在上面的代码中咱们看到ReequestKeys.LeaderAndlst对应的方法其实是KeyhandleLeaderAndIsrRequest。

def handleLeaderAndIsrRequest(request: RequestChannel.Request) {
    // ensureTopicExists is only for client facing requests
    // We can't have the ensureTopicExists check here since the controller sends it as an advisory to all brokers so they
    // stop serving data to clients for the topic being deleted
    val leaderAndIsrRequest = request.requestObj.asInstanceOf[LeaderAndIsrRequest]
    try {
      val (response, error) = replicaManager.becomeLeaderOrFollower(leaderAndIsrRequest, offsetManager)
      val leaderAndIsrResponse = new LeaderAndIsrResponse(leaderAndIsrRequest.correlationId, response, error)
      requestChannel.sendResponse(new Response(request, new BoundedByteBufferSend(leaderAndIsrResponse)))
    } catch {
      case e: KafkaStorageException =>
        fatal("Disk error during leadership change.", e)
        Runtime.getRuntime.halt(1)
    }
  }

将request.requestObj转换成LeaderAndIstRequest对象类型。



Sample Flowchart Template.png

流程图说明

1、如果请求中controllerEpoch小于当前最新的controllerEpoch,则直接返回ErrorMapping.StaleControllerEpochCode。

2、如果partitionStateInfo中的leader epoch大于当前ReplicManager中存储的(topic, partitionId)对应的partition的leader epoch,则:

2.1、如果当前brokerid(或者说replica id)在partitionStateInfo中,则将该partition及partitionStateInfo存入一个名为partitionState的HashMap中。
否则说明该Broker不在该Partition分配的Replica list中,将该信息记录于log中

3、如果partitionStateInfo中的leader epoch小于当前ReplicManager则将相应的Error code(ErrorMapping.StaleLeaderEpochCode)存入Response中。

4、筛选出partitionState中Leader与当前Broker ID相等的所有记录存入partitionsTobeLeader中,其它记录存入partitionsToBeFollower中。
如果partitionsTobeLeader不为空,则对其执行makeLeaders方。
如果partitionsToBeFollower不为空,则对其执行makeFollowers方法。 

时间: 2024-08-26 16:19:38

跟我学Kafka之Controller控制器详解的相关文章

AngularJS入门教程之控制器详解_AngularJS

AngularJS 控制器 AngularJS 控制器 控制 AngularJS 应用程序的数据.  AngularJS 控制器是常规的 JavaScript 对象. AngularJS 控制器 AngularJS 应用程序被控制器控制. ng-controller 指令定义了应用程序控制器. 控制器是 JavaScript 对象,由标准的 JavaScript 对象的构造函数 创建. AngularJS 实例 <!DOCTYPE html> <html> <head>

[@Controller]4 详解@ModelAttribute

[@Controller]4 详解@ModelAttribute  (2012-06-14 15:44:55)转载▼ 标签:  spring   modelattribute   it 分类: JavaSpring A.@ModelAttribute Annotation that binds a method parameter or method return value to a named model attribute, exposed to a web view. Supported

AngularJS控制器详解及示例代码_AngularJS

AngularJS应用主要依赖于控制器来控制数据在应用程序中的流动.控制器采用ng-controller指令定义.控制器是一个包含属性/属性和JavaScript对象的功能.每个控制器接受$scope参数指定应用程序/模块,由控制器控制. <div ng-app="" ng-controller="studentController"> ... </div> 在这里,我们已经声明采用ng-controller指令的控制器studentCont

[@Controller]3 详解@CookieValue,@PathVariable,@RequestBody,@RequestHeader,@RequestParam

[@Controller]3 详解@CookieValue,@PathVariable,@RequestBody,@RequestHeader,@RequestParam (2012-06-14 15:43:49)转载▼ 标签:  cookievalue   pathvariable   requestbody   requestheader 分类: JavaSpring 下列参数一般都和@RequestMapping配合使用.   A.@CookieValue org.springframew

[@Controller]2 详解@RequestMapping

[@Controller]2 详解@RequestMapping  (2012-06-14 15:41:06)转载▼ 标签:  spring   requestmapping   it 分类: JavaSpring A.@RequestMapping org.springframework.web.bind.annotation.RequestMapping Annotation for mapping web requests onto specific handler classes and

Java Spring MVC 上传下载文件配置及controller方法详解_java

下载: 1.在spring-mvc中配置(用于100M以下的文件下载) <bean class="org.springframework.web.servlet.mvc.annotation.AnnotationMethodHandlerAdapter"> <property name="messageConverters"> <list> <!--配置下载返回类型--> <bean class="or

Android组件必学之TabHost使用方法详解_Android

一.TabHost用法通常情况下我们会通过继承TabActivity,调用getTabHost()获取TabHost实例,下面是具体过程. TabHostActivity.java public class TabHostActivity extends TabActivity { private TabHost tabHost; private Intent certificateIntent; private Intent feeIntent; private Intent scoreInt

ThinkPHP控制器详解_php技巧

在上一课程中,你可能会对ThinkPHP的路由会有一丝丝疑惑,不过没关系,学完本课程,很多事都会豁然开朗. 控制器文件命名遵守IndexController.class.php的方式 控制器的定义 在开始之前,我们还是需要明确一下控制器的定义: <?php namespace Home\Controller; use Think\Controller; class IndexController extends Controller { public function read($id){ ec

Kafka 设计与原理详解

一.Kafka简介 本文综合了我之前写的kafka相关文章,可作为一个全面了解学习kafka的培训学习资料. 转载请注明出处 : 本文链接 1.1 背景历史 当今社会各种应用系统诸如商业.社交.搜索.浏览等像信息工厂一样不断的生产出各种信息,在大数据时代,我们面临如下几个挑战: 如何收集这些巨大的信息 如何分析它 如何及时做到如上两点 以上几个挑战形成了一个业务需求模型,即生产者生产(produce)各种信息,消费者消费(consume)(处理分析)这些信息,而在生产者与消费者之间,需要一个沟通