【Kafka源码】ReplicaManager启动过程
在KafkaServer启动过程的入口中,会启动Replica Manager,众所周知,这是一个副本管理器。replica在Kafka中扮演的角色很重要,是保证消息不丢失的一个重要概念。
replica的个人理解概念如下:producer发送的消息给broker,broker是分为多个partition的,对于同一个partition中的broker,这些机器是有主从的概念的。producer只会向leader写入消息,consumer只会从leader读取消息,(leader负责读写,replica保证消息不丢)。为了保证消息不丢失,follower会定时从leader拉取消息,保持与leader的消息同步。当然,producer可以配置是否需要有follower同步成功,以及需要多少个replica,(即需要多少个ack)才算是消息发送成功。这块看个人的需求。
下面我们看下Replica Manager的启动过程。
一、入口
入口在KafkaServer的start方法中,比较简洁:
replicaManager = new ReplicaManager(config, metrics, time, kafkaMetricsTime, zkUtils, kafkaScheduler, logManager,isShuttingDown)
replicaManager.startup()
我们主要看下ReplicaManager里面都有什么内容。
二、ReplicaManager实例化
我们看看实例化的过程:
/* epoch of the controller that last changed the leader */
@volatile var controllerEpoch: Int = KafkaController.InitialControllerEpoch - 1
private val localBrokerId = config.brokerId
private val allPartitions = new Pool[(String, Int), Partition](valueFactory = Some { case (t, p) =>
new Partition(t, p, time, this)
})
private val replicaStateChangeLock = new Object
val replicaFetcherManager = new ReplicaFetcherManager(config, this, metrics, jTime, threadNamePrefix)
private val highWatermarkCheckPointThreadStarted = new AtomicBoolean(false)
val highWatermarkCheckpoints = config.logDirs.map(dir => (new File(dir).getAbsolutePath, new OffsetCheckpoint(new File(dir, ReplicaManager.HighWatermarkFilename)))).toMap
private var hwThreadInitialized = false
this.logIdent = "[Replica Manager on Broker " + localBrokerId + "]: "
val stateChangeLogger = KafkaController.stateChangeLogger
private val isrChangeSet: mutable.Set[TopicAndPartition] = new mutable.HashSet[TopicAndPartition]()
private val lastIsrChangeMs = new AtomicLong(System.currentTimeMillis())
private val lastIsrPropagationMs = new AtomicLong(System.currentTimeMillis())
val delayedProducePurgatory = DelayedOperationPurgatory[DelayedProduce](
purgatoryName = "Produce", config.brokerId, config.producerPurgatoryPurgeIntervalRequests)
val delayedFetchPurgatory = DelayedOperationPurgatory[DelayedFetch](
purgatoryName = "Fetch", config.brokerId, config.fetchPurgatoryPurgeIntervalRequests)
- 首先是这个controllerEpoch,这个值表示的是leader发生变化时controller的epoch。epoch存储在zk中的/Controller_epoch中。
- 第二步是从配置broker.id中获取当前机器的brokerId。
- 实例化ReplicaFetcherManager,是一个follower从leader拉取消息的管理器,这里面有文章。
- 设置highWatermarkCheckPointThreadStarted为false,为了后续启动相关的线程用。
- 从文件(replication-offset-checkpoint)中获取所有topic和partition的HW,这个文件中存储了每个topic和partition对应的最新的checkPoint对应的offset值。HW表示的是topic的partition对应的最后一次commit的消息的offset值,也是用于消息完整性的保证。
- 定义了isrChangerSet,表示了isr改变顺序的集合。至于isr是干啥的,网上的内容比较多,搜索即可。
- 最后涉及到两个配置,分别是:
- producer.purgatory.purge.interval.requests:默认值1000,用于在procucer的ack设置是-1或者1时,跟踪消息是否添加成功,使用DelayedProduce实现。成功后清除。
- fetch.purgatory.purge.interval.requests:默认值1000,fetch 请求清除时的清除间隔
三、启动ReplicaManager
我们主要看下ReplicaManager的start方法:
def startup() {
// start ISR expiration thread
scheduler.schedule("isr-expiration", maybeShrinkIsr, period = config.replicaLagTimeMaxMs, unit = TimeUnit.MILLISECONDS)
scheduler.schedule("isr-change-propagation", maybePropagateIsrChanges, period = 2500L, unit = TimeUnit.MILLISECONDS)
}
这块主要启动了两个定时任务,分别是maybeShrinkIsr和maybePropagateIsrChanges。下面我们着重分析下。
3.1 maybeShrinkIsr
这个方法的调用时间间隔由配置replica.lag.time.max.ms控制,主要用于检查partition对应的isr列表中是否有心跳过期的isr。
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))
}
这块主要是遍历了所有的partition,每个partition都执行maybeShrinkIsr方法,下面我们进入maybeShrinkIsr,分析下主要做了哪些事情。
def maybeShrinkIsr(replicaMaxLagTimeMs: Long) {
val leaderHWIncremented = inWriteLock(leaderIsrUpdateLock) {
leaderReplicaIfLocal() match {
case Some(leaderReplica) =>
val outOfSyncReplicas = getOutOfSyncReplicas(leaderReplica, replicaMaxLagTimeMs)
if(outOfSyncReplicas.size > 0) {
val newInSyncReplicas = inSyncReplicas -- outOfSyncReplicas
assert(newInSyncReplicas.size > 0)
info("Shrinking ISR for partition [%s,%d] from %s to %s".format(topic, partitionId,
inSyncReplicas.map(_.brokerId).mkString(","), newInSyncReplicas.map(_.brokerId).mkString(",")))
// update ISR in zk and in cache
updateIsr(newInSyncReplicas)
// we may need to increment high watermark since ISR could be down to 1
replicaManager.isrShrinkRate.mark()
maybeIncrementLeaderHW(leaderReplica)
} else {
false
}
case None => false // do nothing if no longer leader
}
}
整个步骤如下:
- leaderReplicaIfLocal:先检查当前的partition的leader是否为当前的broker,如果为是,就不进入下面的方法,否则进入下面的方法。
- getOutOfSyncReplicas:获取不同步的replica列表,获取的方法是首先从isr中去除掉leader,然后把当前时间-lastCaughtUpTimeMs大于replicaMaxLagTimeMs的replica筛选出来,即为outOfSyncReplicas。这里面的lastCaughtUpTimeMs是指上次同步的时间,不一定是心跳时间。
- 如果outOfSyncReplicas中存在replica,则继续。两个列表进行差值运算后得到新的isr列表,之后更新isr列表(即zk中的数据)。
- 最后可能需要更新下HW
3.2 maybePropagateIsrChanges
这个方法的调用时间是固定的,不由配置决定,代码中写死,为2500ms。这个方法会把isr的变化内容更新到zk中去,执行这个方法的条件是:
- ISR变化没有被广播出去
- 最近5s内没有ISR变化或者上次广播的时间距离当前时间超过了60s,其实这里的广播就是指写入到zk中
def maybePropagateIsrChanges() {
val now = System.currentTimeMillis()
isrChangeSet synchronized {
if (isrChangeSet.nonEmpty &&
(lastIsrChangeMs.get() + ReplicaManager.IsrChangePropagationBlackOut < now ||
lastIsrPropagationMs.get() + ReplicaManager.IsrChangePropagationInterval < now)) {
ReplicationUtils.propagateIsrChanges(zkUtils, isrChangeSet)
isrChangeSet.clear()
lastIsrPropagationMs.set(now)
}
}
}
【Kafka源码】ReplicaManager启动过程的更多相关文章
- Symfony2源码分析——启动过程2
文章地址:http://www.hcoding.com/?p=46 上一篇分析Symfony2框架源码,探究Symfony2如何完成一个请求的前半部分,前半部分可以理解为Symfony2框架为处理请求 ...
- quartz2.x源码分析——启动过程
title: quartz2.x源码分析--启动过程 date: 2017-04-13 14:59:01 categories: quartz tags: [quartz, 源码分析] --- 先简单 ...
- mysql源码分析-启动过程
mysql源码分析-启动过程 概要 # sql/mysqld.cc, 不包含psi的初始化过程 mysqld_main: // 加载my.cnf和my.cnf.d,还有命令行参数 if (load_d ...
- Nginx学习笔记(六) 源码分析&启动过程
Nginx的启动过程 主要介绍Nginx的启动过程,可以在/core/nginx.c中找到Nginx的主函数main(),那么就从这里开始分析Nginx的启动过程. 涉及到的基本函数 源码: /* * ...
- Symfony2源码分析——启动过程1
本文通过阅读分析Symfony2的源码,了解Symfony2启动过程中完成哪些工作,从阅读源码了解Symfony2框架. Symfony2的核心本质是把Request转换成Response的一个过程. ...
- 分布式事务_02_2PC框架raincat源码解析-启动过程
一.前言 上一节已经将raincat demo工程运行起来了,这一节来分析下raincat启动过程的源码 主要包括: 事务协调者启动过程 事务参与者启动过程 二.协调者启动过程 主要就是在启动类中通过 ...
- Spring MVC源码(一) ----- 启动过程与组件初始化
SpringMVC作为MVC框架近年来被广泛地使用,其与Mybatis和Spring的组合,也成为许多公司开发web的套装.SpringMVC继承了Spring的优点,对业务代码的非侵入性,配置的便捷 ...
- Spring Boot源码分析-启动过程
Spring Boot作为目前最流行的Java开发框架,秉承"约定优于配置"原则,大大简化了Spring MVC繁琐的XML文件配置,基本实现零配置启动项目. 本文基于Spring ...
- Syncthing源码解析 - 启动过程
我相信很多朋友会认为启动就是双击一下Syncthing程序图标,随后就启动完毕了!如果这样认为,对,也不对!对,是因为的确是这样操作,启动了Syncthing:不对是因为在调试Syncthing启动过 ...
- Redis源码研究--启动过程
---------------------6月23日--------------------------- Redis启动入口即main函数在redis.c文件,伪代码如下: int main(int ...
随机推荐
- linux学习笔记:1.基础知识和命令行基本操作
初次学习linux系统,想在这里记录自己的学习痕迹,如发现有不足之处,希望能指出,谢谢啦,之后的学习是在虚拟机VMware 10下的Red Hat Enterprise linux 6 的操作. 一. ...
- 使用vs编译事件来动态发布配置文件
我们知道开发有很多的环境,一般我们会分为开发环境,测试环境,生产环境.而我们使用的vs默认配置就两种:Debug和Release.当然vs支持通过配置管理器来添加,编辑及删除配置. 为此不同的环境和配 ...
- http://codeforces.com/contest/828
哇这是我打的第一场cf,第一题都wa了无数次,然后第二题差几分钟交 ,第二天一交就AC了内心是崩溃的.果然我还是太菜l.... A. Restaurant Tables time limit per ...
- 记录一下从懵懂到理解RESTful的过程
前言 Spring+SpringMVC+MyBatis+easyUI整合进阶篇(一)设计一套好的RESTful API Spring+SpringMVC+MyBatis+easyUI整合进阶篇(二)R ...
- 项目管理软件之争,禅道和JIRA大对比
本文摘要: 一. 产品介绍 二. 界面设计 1. 界面颜色设计 2. 布局结构 三. 功能区别 四. 价格对比 五. 后期服务 六. 优缺点 七. 总结 说到项目管理软件,不得不提的是禅道和JIRA. ...
- 使用jsonp完美解决跨域问题
调用web接口,get请求,发现提示:No 'Access-Control-Allow-Origin' header is present on the requested resource. 这个和 ...
- Node.js之循环依赖
在Node.js中有可能会出现循环依赖的问题,在此做一个简单的记录 假如有一个模块A: exports.loaded = false; const b = require('./b'); module ...
- C#.net干货,最全公共帮助类
比较全面的c#帮助类,日常工作收集,包括前面几家公司用到的,各式各样的几乎都能找到,所有功能性代码都是独立的类,类与类之间没有联系,可以单独引用至项目,分享出来,方便大家,几乎都有注释,喜欢的请点赞, ...
- 【转】python数据格式化之pprint
pprint – 美观打印 作用:美观打印数据结构 pprint 包含一个“美观打印机”,用于生成数据结构的一个美观视图.格式化工具会生成数据结构的一些表示,不仅可以由解释器正确地解析,而且便于人类阅 ...
- ubuntu环境下lnmp环境搭建(1)之Mysql
1. vm下安装Ubuntu 1)下载镜像ubuntu-15.04-desktop-amd64.iso http://yunpan.cn/cF5dwV6zw33ef 访问密码 ecba(个人分享在36 ...