最近领导将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顺序
- 状态 NotRunning -> Starting
- initZkClient //初始化Z看
- getOrGenerateClusterId //在ZK 上初始化clusterID
- getBrokerIdAndOfflineDirs //生成brokerID
- KafkaScheduler.startup //初始化全局定时器
- quotaManagers = QuotaFactory.instantiate //初始化配额管理服务
- logManager.startup() //初始化日志管理服务
- 状态 Starting -> RecoveringFromUncleanShutdown // 如果需要
- new MetadataCache //新建元数据缓存
- new SocketServer //新建RPC服务
- createReplicaManager //创建副本管理服务
- kafkaController.startup() //初始化控制器服务
- new AdminManager //新建管理员命令服务
- groupCoordinator.startup() //消费者组协调者器
- transactionCoordinator.startup() //事务协调者
- new KafkaApis //新建请求服务类
- new KafkaRequestHandlerPool //RPCServer请求的处理线程
- dynamicConfigHandlers //动态配置处理类
- new DynamicConfigManager //动态配置管理
- socketServer.startProcessors() //启动RPC服务
- 状态 Starting/RecoveringFromUncleanShutdown -> RunningAsBroker
shutdown顺序
- 状态 RunningAsBroker -> BrokerShuttingDown
- dynamicConfigManager.shutdown()
- socketServer.stopProcessingRequests()
- requestHandlerPool.shutdown()
- kafkaScheduler.shutdown()
- apis.close()
- adminManager.shutdown()
- transactionCoordinator.shutdown()
- groupCoordinator.shutdown()
- tokenManager.shutdown()
- replicaManager.shutdown()
- adminManager.shutdown()
- groupCoordinator.shutdown()
- logManager.shutdown()
- logManager.shutdown()
- zkUtils.close()
- metrics.close()
- 状态 BrokerShuttingDown -> NotRunning
总结的说:
日志管理服务logManager和副本管理replicaManager共享了同一定时器kafkaScheduler,transactionCoordinator自己独立维护了一个定时器,初始线程数时1.
对于服务的停止,broker使用了一个CountDownLatch来做标示,个人感觉没有必要,完全可以是一个AtomicBoolean能解决的事情。