Kafka源码分析
本文主要针对于Kafka的源码进行分析,版本为kafka-0.8.2.1。 由于时间有限,可能更新比较慢...
Kafka.scala
// 读取配置文件
val props = Utils.loadProps(args(0))
val serverConfig = new KafkaConfig(props)
KafkaMetricsReporter.startReporters(serverConfig.props) val kafkaServerStartable = new KafkaServerStartable(serverConfig) // 注册一个关闭钩子,当JVM关闭时调用KafkaServerStartable.shutdown
Runtime.getRuntime().addShutdownHook(new Thread() {
override def run() = kafkaServerStartable.shutdown
}) // 运行并等待结束
kafkaServerStartable.startup
kafkaServerStartable.awaitShutdown
Server
实际调用类为KafkaServer
def startup() {
kafkaScheduler.startup()
// 初始化Zookeeper内相关路径
zkClient = initZk()
// 日志管理器
logManager = createLogManager(zkClient, brokerState)
logManager.startup()
socketServer = new SocketServer(...)
socketServer.startup()
// 启动副本管理器
replicaManager = new ReplicaManager(config, time, zkClient, kafkaScheduler, logManager, isShuttingDown)
// 创建偏移量管理器
offsetManager = createOffsetManager()
// 实例化调度器
kafkaController = new KafkaController(config, zkClient, brokerState)
// 请求处理器
apis = new KafkaApis(...)
// 网络请求处理
requestHandlerPool = new KafkaRequestHandlerPool(config.brokerId, socketServer.requestChannel, apis, config.numIoThreads)
brokerState.newState(RunningAsBroker)
Mx4jLoader.maybeLoad()
replicaManager.startup()
kafkaController.startup()
// Topic配置管理器
topicConfigManager = new TopicConfigManager(zkClient, logManager)
topicConfigManager.startup()
// Broker的心跳检查
kafkaHealthcheck = new KafkaHealthcheck(...)
kafkaHealthcheck.startup()
registerStats()
startupComplete.set(true)
info("started")
}
在KafkaServer的startup中看到主要进行几个主要服务的初始化和启动。
private def initZk(): ZkClient =
{
info("Connecting to zookeeper on " + config.zkConnect) // Kafka在Zookeeper中的工作根目录
val chroot = {
if (config.zkConnect.indexOf("/") > 0)
config.zkConnect.substring(config.zkConnect.indexOf("/"))
else
""
}
// 创建工作根目录
if (chroot.length > 1) {
val zkConnForChrootCreation = config.zkConnect.substring(0, config.zkConnect.indexOf("/"))
val zkClientForChrootCreation = new ZkClient(...)
ZkUtils.makeSurePersistentPathExists(zkClientForChrootCreation, chroot)
info("Created zookeeper path " + chroot)
zkClientForChrootCreation.close()
} // 实例化ZkClient
val zkClient = new ZkClient(config.zkConnect, config.zkSessionTimeoutMs, config.zkConnectionTimeoutMs, ZKStringSerializer)
// 在Zookeeper中创建必要持久路径
ZkUtils.setupCommonPaths(zkClient)
zkClient
}
KafkaScheduler实际为对线程池ScheduledThreadPoolExecutor的封装,这里不做过多的分析。
KafkaHealthcheck(...)
{
val brokerIdPath = ZkUtils.BrokerIdsPath + "/" + brokerId
val sessionExpireListener = new SessionExpireListener def startup()
{
// 注册一个Zookeeper事件(状态)监听器
zkClient.subscribeStateChanges(sessionExpireListener)
// 在Zookeeper的/brokers/ids/id目录创建临时节点并写入节点信息
register()
}
}
IZkStateListener 定义了两种事件:一种是连接状态的改变,例如由未连接改变成连接上,连接上改为过期等;
另一种创建一个新的session(连接), 通常是由于session失效然后新的session被建立时触发。
class SessionExpireListener() extends IZkStateListener
{
@throws(classOf[Exception])
def handleStateChanged(state: KeeperState) {} @throws(classOf[Exception])
def handleNewSession() = register()
}
ReplicaManager
def startup()
{
scheduler.schedule("isr-expiration", maybeShrinkIsr, period = config.replicaLagTimeMaxMs, unit = TimeUnit.MILLISECONDS)
}
// 定时调用maybeShrinkIsr
private def maybeShrinkIsr(): Unit =
{
trace("Evaluating ISR list of partitions to see which replicas can be removed from the ISR")
allPartitions.values.foreach(partition => partition.maybeShrinkIsr(config.replicaLagTimeMaxMs, config.replicaLagMaxMessages))
}
这里调用了cluster.Partition中的maybeShrinkIsr来将卡住的或者低效的副本从ISR中去除并更新HighWatermark。
def maybeShrinkIsr(replicaMaxLagTimeMs: Long, replicaMaxLagMessages: Long)
{
inWriteLock(leaderIsrUpdateLock) {
leaderReplicaIfLocal() match {
case Some(leaderReplica) =>
// 找出卡住和低效的Replica并从ISR中去除
val outOfSyncReplicas = getOutOfSyncReplicas(leaderReplica, replicaMaxLagTimeMs, replicaMaxLagMessages)
if(outOfSyncReplicas.size > 0) {
val newInSyncReplicas = inSyncReplicas -- outOfSyncReplicas
assert(newInSyncReplicas.size > 0)
// 更新ZK中的ISR
updateIsr(newInSyncReplicas)
// 计算HW并更新
maybeIncrementLeaderHW(leaderReplica)
replicaManager.isrShrinkRate.mark()
}
...
}
def getOutOfSyncReplicas(leaderReplica: Replica, keepInSyncTimeMs: Long, keepInSyncMessages: Long): Set[Replica] =
{
// Leader的最后写入偏移量
val leaderLogEndOffset = leaderReplica.logEndOffset
// ISR中排除LeaderReplica的其他集合
val candidateReplicas = inSyncReplicas - leaderReplica
// 卡住的Replica集合
val stuckReplicas = candidateReplicas.filter(r => (time.milliseconds - r.logEndOffsetUpdateTimeMs) > keepInSyncTimeMs)
// 低效的Replica
// 条件1 Replicas的offset > 0
// 条件2 Leader的offset - Replicas的offset > 阀值
val slowReplicas = candidateReplicas.filter(r =>
r.logEndOffset.messageOffset >= 0 &&
leaderLogEndOffset.messageOffset - r.logEndOffset.messageOffset > keepInSyncMessages)
// 返回卡住的和低效的Replicas
stuckReplicas ++ slowReplicas
}
Cluster
Controller
Kafka源码分析的更多相关文章
- Apache Kafka源码分析 – Broker Server
1. Kafka.scala 在Kafka的main入口中startup KafkaServerStartable, 而KafkaServerStartable这是对KafkaServer的封装 1: ...
- Kafka源码分析系列-目录(收藏不迷路)
持续更新中,敬请关注! 目录 <Kafka源码分析>系列文章计划按"数据传递"的顺序写作,即:先分析生产者,其次分析Server端的数据处理,然后分析消费者,最后再补充 ...
- Kafka源码分析(一) - 概述
系列文章目录 https://zhuanlan.zhihu.com/p/367683572 目录 系列文章目录 一. 实际问题 二. 什么是Kafka, 如何解决这些问题的 三. 基本原理 1. 基本 ...
- Kafka源码分析(三) - Server端 - 消息存储
系列文章目录 https://zhuanlan.zhihu.com/p/367683572 目录 系列文章目录 一. 业务模型 1.1 概念梳理 1.2 文件分析 1.2.1 数据目录 1.2.2 . ...
- kafka源码分析之一server启动分析
0. 关键概念 关键概念 Concepts Function Topic 用于划分Message的逻辑概念,一个Topic可以分布在多个Broker上. Partition 是Kafka中横向扩展和一 ...
- apache kafka源码分析-Producer分析---转载
原文地址:http://www.aboutyun.com/thread-9938-1-1.html 问题导读1.Kafka提供了Producer类作为java producer的api,此类有几种发送 ...
- Kafka源码分析及图解原理之Producer端
一.前言 任何消息队列都是万变不离其宗都是3部分,消息生产者(Producer).消息消费者(Consumer)和服务载体(在Kafka中用Broker指代).那么本篇主要讲解Producer端,会有 ...
- Kafka源码分析(二) - 生产者
系列文章目录 https://zhuanlan.zhihu.com/p/367683572 目录 系列文章目录 一. 使用方式 step 1: 设置必要参数 step 2: 创建KafkaProduc ...
- Kafka源码分析-序列2 -Producer
在上一篇,我们从使用方式和策略上,对消息队列做了一个宏观描述.从本篇开始,我们将深入到源码内部,仔细分析Kafka到底是如何实现一个分布式消息队列.我们的分析将从Producer端开始. 从Kafka ...
随机推荐
- ubnut16.4 下 设置静态ip
一.编辑interfaces sudo vi /etc/network/interface ### i 键进入编辑 二.编辑该文件 auto loiface lo inet loopbackauto ...
- Appium+python(1)简单的介绍环境搭建
环境搭建其实并不难,只不过安装的东西有点多,要加的环境变量有点多. 链接:https://pan.baidu.com/s/1nwLhNIT 密码:56wn 这个压缩包里要用的都有了,只需要下载,然后安 ...
- Redis事务及锁应用
Redis只支持简单的事务,不像mysql那样比较完整严格,对数据的完整性也维持的很好.redis的开启事务实际上只是将开启事务之后的一段命令用队列包裹起来了,当调用redis的执行命令(exec)全 ...
- Linux的POSIX线程属性
创建POSIX线程的函数为 int pthread_create(pthread_t *thread, const pthread_attr_t *attr, void *(*start_routin ...
- nginx和php-fpm通信的两种方式 unix socket和TCP
nginx和fastcgi的通信方式有两种,一种是TCP 一种是unix socket TCP使用的是 127.0.0.1:9000端口,将fastcgi_pass参数修改为127.0.0.1:900 ...
- mysql索引之一:索引基础(B-Tree索引、哈希索引、聚簇索引、全文(Full-text)索引区别)(唯一索引、最左前缀索引、前缀索引、多列索引)
没有索引时mysql是如何查询到数据的 索引对查询的速度有着至关重要的影响,理解索引也是进行数据库性能调优的起点.考虑如下情况,假设数据库中一个表有10^6条记录,DBMS的页面大小为4K,并存储10 ...
- IJ
ylbtech-IJ: 1.返回顶部 2.返回顶部 3.返回顶部 4.返回顶部 5.返回顶部 6.返回顶部 7.返回顶部 8.返回顶部 9.返回顶部 10.返回 ...
- 小程序picker组件当range是objectArray时的使用
- nfs之脚气
nfs工作模式参考 一.NFs是什么 NFS是Network File System的缩写,即网络文件系统.客户端通过挂载的方式将NFS服务器端共享的数据目录挂载到本地目录下. nfs为什么需要RPC ...
- 【BZOJ】2809: [Apio2012]dispatching(左偏树)
题目 传送门:QWQ 分析 显然是一个资瓷合并的堆 现学了一发左偏树:教程 然后就没了 代码 #include <bits/stdc++.h> #define lc son[x][0] # ...