最近领导将kafka交给我维护,平时运行比较稳定,偶尔还是会出问题。由于以前只停留在kafka api调用和简单原理层级,对生产kafka出现问题很难定位,经常很尴尬被动。本人不喜欢被动,所以打算抽一周时间将kafka源码简单通读一遍。提升对kafka的认知,便于更快更准得定位解决问题!

目前的公司使用kafka1.0,所以这次计划做一个kafka1.0的详细介绍,会涉及里面的一些代码实现。

kafka的broker节点大致包涵了一下模块

  • zk连接器:KafkaZkClient
  • 动态配置管理:dynamicConfigManager,dynamicConfigHandlers,这个是为了解决修改配置需要重启的问题,提取了部分配置,并不是所有配置都可以不重启生效。依赖了zkClient
  • 定时调度器:kafkaScheduler,该调度器是全局共享的。 承担了logManager模块中的日志归档,日志刷新、检查点恢复,日志的offset检查,过期日志的删除。ReplicaManager模块中的高水位检查,ISR过期检查,ISR变更处理,空闲ISR停止
  • 配额管理:quotaManagers ,限制了客户端生产、消费者消费、副本复制的流控。
  • 日志管理服务:logManager,logDirFailureChannel ,主要是本地日志的生成,清理,滚动。依赖了kafkaScheduler,KafkaZkClient
  • 元数据管理:metadataCache ,顾名思义就是针对topic元数据的缓存
  • RPC服务:socketServer,接口RPC请求的服务器。连接管理,请求分发和响应消息返回。新增了单IP的请求限制及溢出水位控制。
  • 副本管理:replicaManager ,副本的追加日志、删除日志,ISR的各种状态变更检查。依赖了KafkaZkClient,kafkaScheduler,logManager,quotaManagers,metadataCache。
  • 请求认证管理:tokenManager ,这个是权限相关,如果不开启的话,一般不用。
  • 控制器:kafkaController,这个是非常核心的,全集群唯一的控制器,控制所有的元数据变更管理,具体内容单章来说明。依赖了tokenManager,KafkaZkClient
  • 管理命令服务:adminManager,针对管理员命令的服务处理,命令行方式的创建topic,删除topic,增加分区等。依赖了 metadataCache,KafkaZkClient模块
  • 消费者协调者:groupCoordinator ,每个消费组,在服务都需要一个全局的消费组协调者,解决消费者的rebalance问题。依赖了replicaManager,KafkaZkClient模块
  • 事务协调者:transactionCoordinator,数据提交写入到本地,到复制到其他节点等全部环节中的事务管理器。依赖了replicaManager,metadataCache。
  • 请求处理线程池:requestHandlerPool , 处理来之socketServer中的请求,再调用KafkaApis处理具体的数据。依赖了socketServer,apis。
  • 业务处理服务:KafkaApis,所有请求的处理入口。依赖了socketServer,replicaManager,adminManager,groupCoordinator,transactionCoordinator,fetchManager,tokenManager.可以说这个类是整个集群的统一处理的地方。

整个服务的状态流程是通过BrokerState类来控制。

启动流转过程是:NotRunning -> Starting -> RecoveringFromUncleanShutdown(非必要) -> RunningAsBroker .

停止流转过程是:RunningAsBroker -> BrokerShuttingDown -> PendingControlledShutdown -> NotRunning

服务的启动:startup

服务的停止:shutdown

其他方法都是延伸自这两个方法

startup顺序

  1. 状态 NotRunning -> Starting
  2. initZkClient //初始化Z看
  3. getOrGenerateClusterId //在ZK 上初始化clusterID
  4. getBrokerIdAndOfflineDirs //生成brokerID
  5. KafkaScheduler.startup //初始化全局定时器
  6. quotaManagers = QuotaFactory.instantiate //初始化配额管理服务
  7. logManager.startup() //初始化日志管理服务
  8. 状态 Starting -> RecoveringFromUncleanShutdown  // 如果需要
  9. new MetadataCache //新建元数据缓存
  10. new SocketServer //新建RPC服务
  11. createReplicaManager //创建副本管理服务
  12. kafkaController.startup() //初始化控制器服务
  13. new AdminManager //新建管理员命令服务
  14. groupCoordinator.startup() //消费者组协调者器
  15. transactionCoordinator.startup() //事务协调者
  16. new KafkaApis //新建请求服务类
  17. new KafkaRequestHandlerPool //RPCServer请求的处理线程
  18. dynamicConfigHandlers //动态配置处理类
  19. new DynamicConfigManager //动态配置管理
  20. socketServer.startProcessors() //启动RPC服务
  21. 状态 Starting/RecoveringFromUncleanShutdown -> RunningAsBroker

shutdown顺序

  1. 状态 RunningAsBroker -> BrokerShuttingDown
  2. dynamicConfigManager.shutdown()
  3. socketServer.stopProcessingRequests()
  4. requestHandlerPool.shutdown()
  5. kafkaScheduler.shutdown()
  6. apis.close()
  7. adminManager.shutdown()
  8. transactionCoordinator.shutdown()
  9. groupCoordinator.shutdown()
  10. tokenManager.shutdown()
  11. replicaManager.shutdown()
  12. adminManager.shutdown()
  13. groupCoordinator.shutdown()
  14. logManager.shutdown()
  15. logManager.shutdown()
  16. zkUtils.close()
  17. metrics.close()
  18. 状态 BrokerShuttingDown -> NotRunning

总结的说:

日志管理服务logManager和副本管理replicaManager共享了同一定时器kafkaScheduler,transactionCoordinator自己独立维护了一个定时器,初始线程数时1.

对于服务的停止,broker使用了一个CountDownLatch来做标示,个人感觉没有必要,完全可以是一个AtomicBoolean能解决的事情。