Apache Kafka源码分析 – ReplicaManager
如果说controller作为master,负责全局的事情,比如选取leader,reassignment等
那么ReplicaManager就是worker,负责完成replica的管理工作
主要工作包含,
stopReplica
getOrCreatePartition
getLeaderReplicaIfLocal
getReplica
readMessageSets
becomeLeaderOrFollower
StopReplicaCommand
处理很简单,主要就是停止fetcher线程,并删除partition目录
stopReplicas
def stopReplicas(stopReplicaRequest: StopReplicaRequest): (mutable.Map[TopicAndPartition, Short], Short) = {
replicaStateChangeLock synchronized { // 加锁
val responseMap = new collection.mutable.HashMap[TopicAndPartition, Short]
if(stopReplicaRequest.controllerEpoch < controllerEpoch) { // 检查Epoch,防止收到过期的request
(responseMap, ErrorMapping.StaleControllerEpochCode)
} else {
controllerEpoch = stopReplicaRequest.controllerEpoch // 更新Epoch
// First stop fetchers for all partitions, then stop the corresponding replicas
replicaFetcherManager.removeFetcherForPartitions(stopReplicaRequest.partitions.map(r => TopicAndPartition(r.topic, r.partition))) // 先通过FetcherManager停止相关partition的Fetcher线程
for(topicAndPartition <- stopReplicaRequest.partitions){
val errorCode = stopReplica(topicAndPartition.topic, topicAndPartition.partition, stopReplicaRequest.deletePartitions) // 调用stopReplica
responseMap.put(topicAndPartition, errorCode)
}
(responseMap, ErrorMapping.NoError)
}
}
}
stopReplica
def stopReplica(topic: String, partitionId: Int, deletePartition: Boolean): Short = {
getPartition(topic, partitionId) match {
case Some(partition) =>
leaderPartitionsLock synchronized {
leaderPartitions -= partition
}
if(deletePartition) { // 仅仅在deletePartition=true时,才会真正删除该partition
val removedPartition = allPartitions.remove((topic, partitionId))
if (removedPartition != null)
removedPartition.delete() // this will delete the local log
}
case None => //do nothing if replica no longer exists. This can happen during delete topic retries
}
}
LeaderAndISRCommand
becomeLeaderOrFollower
做些epoch和valid的检查,然后区分出leader和follows,分别调用makeLeaders,makeFollowers
def becomeLeaderOrFollower(leaderAndISRRequest: LeaderAndIsrRequest): (collection.Map[(String, Int), Short], Short) = {
replicaStateChangeLock synchronized {// 加锁
val responseMap = new collection.mutable.HashMap[(String, Int), Short]
if(leaderAndISRRequest.controllerEpoch < controllerEpoch) { // 检查requset epoch
(responseMap, ErrorMapping.StaleControllerEpochCode)
} else {
val controllerId = leaderAndISRRequest.controllerId
val correlationId = leaderAndISRRequest.correlationId
controllerEpoch = leaderAndISRRequest.controllerEpoch // First check partition's leader epoch // 前面只是检查了request的epoch,但是还要检查其中的每个partitionStateInfo中的leader epoch
val partitionState = new HashMap[Partition, PartitionStateInfo]()
leaderAndISRRequest.partitionStateInfos.foreach{ case ((topic, partitionId), partitionStateInfo) =>
val partition = getOrCreatePartition(topic, partitionId, partitionStateInfo.replicationFactor) // get或创建partition
val partitionLeaderEpoch = partition.getLeaderEpoch()
// If the leader epoch is valid record the epoch of the controller that made the leadership decision.
// This is useful while updating the isr to maintain the decision maker controller's epoch in the zookeeper path
if (partitionLeaderEpoch < partitionStateInfo.leaderIsrAndControllerEpoch.leaderAndIsr.leaderEpoch) { // local的partitionLeaderEpoch要小于request中的leaderEpoch,否则就是过时的request
if(partitionStateInfo.allReplicas.contains(config.brokerId)) // 判断该partition是否被assigned给当前的broker
partitionState.put(partition, partitionStateInfo)
else { }
} else { // Received invalid LeaderAndIsr request
// Otherwise record the error code in response
responseMap.put((topic, partitionId), ErrorMapping.StaleLeaderEpochCode)
}
} val partitionsTobeLeader = partitionState
.filter{ case (partition, partitionStateInfo) => partitionStateInfo.leaderIsrAndControllerEpoch.leaderAndIsr.leader == config.brokerId}
val partitionsToBeFollower = (partitionState -- partitionsTobeLeader.keys) if (!partitionsTobeLeader.isEmpty) makeLeaders(controllerId, controllerEpoch, partitionsTobeLeader, leaderAndISRRequest.correlationId, responseMap)
if (!partitionsToBeFollower.isEmpty) makeFollowers(controllerId, controllerEpoch, partitionsToBeFollower, leaderAndISRRequest.leaders, leaderAndISRRequest.correlationId, responseMap) // we initialize highwatermark thread after the first leaderisrrequest. This ensures that all the partitions
// have been completely populated before starting the checkpointing there by avoiding weird race conditions
if (!hwThreadInitialized) {
startHighWaterMarksCheckPointThread() // 启动HighWaterMarksCheckPointThread
hwThreadInitialized = true
}
replicaFetcherManager.shutdownIdleFetcherThreads()
(responseMap, ErrorMapping.NoError)
}
}
}
makeLeaders
停止Fetcher,调用partition.makeLeader,把这些partition加到leaderPartitions中
/*
* Make the current broker to become leader for a given set of partitions by:
*
* 1. Stop fetchers for these partitions
* 2. Update the partition metadata in cache
* 3. Add these partitions to the leader partitions set
*
* If an unexpected error is thrown in this function, it will be propagated to KafkaApis where
* the error message will be set on each partition since we do not know which partition caused it
* TODO: the above may need to be fixed later
*/
private def makeLeaders(controllerId: Int, epoch: Int,
partitionState: Map[Partition, PartitionStateInfo],
correlationId: Int, responseMap: mutable.Map[(String, Int), Short]) = {
try {
// First stop fetchers for all the partitions
replicaFetcherManager.removeFetcherForPartitions(partitionState.keySet.map(new TopicAndPartition(_)))
// Update the partition information to be the leader
partitionState.foreach{ case (partition, partitionStateInfo) =>
partition.makeLeader(controllerId, partitionStateInfo, correlationId)} // Finally add these partitions to the list of partitions for which the leader is the current broker
leaderPartitionsLock synchronized {
leaderPartitions ++= partitionState.keySet
}
} catch {
}
}
makeFollowers
除了修改leaderPartitions和Mark as followers以外
作为followers,需要truncated log到highWatermark,然后启动fetcher去catch leader
/*
* Make the current broker to become follower for a given set of partitions by:
*
* 1. Remove these partitions from the leader partitions set.
* 2. Mark the replicas as followers so that no more data can be added from the producer clients.
* 3. Stop fetchers for these partitions so that no more data can be added by the replica fetcher threads.
* 4. Truncate the log and checkpoint offsets for these partitions.
* 5. If the broker is not shutting down, add the fetcher to the new leaders.
*
* The ordering of doing these steps make sure that the replicas in transition will not
* take any more messages before checkpointing offsets so that all messages before the checkpoint
* are guaranteed to be flushed to disks
*
* If an unexpected error is thrown in this function, it will be propagated to KafkaApis where
* the error message will be set on each partition since we do not know which partition caused it
*/
private def makeFollowers(controllerId: Int, epoch: Int, partitionState: Map[Partition, PartitionStateInfo],
leaders: Set[Broker], correlationId: Int, responseMap: mutable.Map[(String, Int), Short]) {
try {
leaderPartitionsLock synchronized {
leaderPartitions --= partitionState.keySet
} partitionState.foreach{ case (partition, leaderIsrAndControllerEpoch) =>
partition.makeFollower(controllerId, leaderIsrAndControllerEpoch, leaders, correlationId)} replicaFetcherManager.removeFetcherForPartitions(partitionState.keySet.map(new TopicAndPartition(_))) logManager.truncateTo(partitionState.map{ case(partition, leaderISRAndControllerEpoch) => // 将当前replica的log truncate到highWatermark,因为只有committed的数据是可以保证和leader一致的
new TopicAndPartition(partition) -> partition.getOrCreateReplica().highWatermark
})
if (!isShuttingDown.get()) { // 如果该broker没有shutting down
val partitionAndOffsets = mutable.Map[TopicAndPartition, BrokerAndInitialOffset]()
partitionState.foreach {
case (partition, partitionStateInfo) =>
val leader = partitionStateInfo.leaderIsrAndControllerEpoch.leaderAndIsr.leader // 找到leader
leaders.find(_.id == leader) match {
case Some(leaderBroker) =>
partitionAndOffsets.put(new TopicAndPartition(partition), // get当前replica的logEndOffset
BrokerAndInitialOffset(leaderBroker, partition.getReplica().get.logEndOffset))
case None =>
}
}
replicaFetcherManager.addFetcherForPartitions(partitionAndOffsets) // 启动Fetcher去catch leader
}
else { }
}
} catch {
}
}
checkpointHighWatermarks
对于每个replica而已,HighWatermarks是很重要的,因为只有通过它可以知道到底哪些数据是一致的,这样就算broker crash,恢复的时候只需要基于HighWatermarks继续catch就可以
所以对于HighWatermarks,需要做cp
/**
* Flushes the highwatermark value for all partitions to the highwatermark file
*/
def checkpointHighWatermarks() {
val replicas = allPartitions.values.map(_.getReplica(config.brokerId)).collect{case Some(replica) => replica}
val replicasByDir = replicas.filter(_.log.isDefined).groupBy(_.log.get.dir.getParent)
for((dir, reps) <- replicasByDir) {
val hwms = reps.map(r => (new TopicAndPartition(r) -> r.highWatermark)).toMap
try {
highWatermarkCheckpoints(dir).write(hwms)
} catch {
case e: IOException =>
fatal("Error writing to highwatermark file: ", e)
Runtime.getRuntime().halt(1)
}
}
}
Apache Kafka源码分析 – ReplicaManager的更多相关文章
- Apache Kafka源码分析 – Broker Server
1. Kafka.scala 在Kafka的main入口中startup KafkaServerStartable, 而KafkaServerStartable这是对KafkaServer的封装 1: ...
- apache kafka源码分析-Producer分析---转载
原文地址:http://www.aboutyun.com/thread-9938-1-1.html 问题导读1.Kafka提供了Producer类作为java producer的api,此类有几种发送 ...
- Apache Kafka源码分析 - kafka controller
前面已经分析过kafka server的启动过程,以及server所能处理的所有的request,即KafkaApis 剩下的,其实关键就是controller,以及partition和replica ...
- Apache Kafka源码分析 - KafkaApis
kafka apis反映出kafka broker server可以提供哪些服务,broker server主要和producer,consumer,controller有交互,搞清这些api就清楚了 ...
- Apache Kafka源码分析 – Replica and Partition
Replica 对于local replica, 需要记录highWatermarkValue,表示当前已经committed的数据对于remote replica,需要记录logEndOffsetV ...
- Apache Kafka源码分析 – Controller
https://cwiki.apache.org/confluence/display/KAFKA/Kafka+Controller+Internalshttps://cwiki.apache.org ...
- Apache Kafka源码分析 – Log Management
LogManager LogManager会管理broker上所有的logs(在一个log目录下),一个topic的一个partition对应于一个log(一个log子目录)首先loadLogs会加载 ...
- Apache Kafka源码分析 - autoLeaderRebalanceEnable
在broker的配置中,auto.leader.rebalance.enable (false) 那么这个leader是如何进行rebalance的? 首先在controller启动的时候会打开一个s ...
- Apache Kafka源码分析 - ReplicaStateMachine
startup 在onControllerFailover中被调用, /** * Invoked on successful controller election. First registers ...
随机推荐
- Using JWT with Spring Security OAuth
http://www.baeldung.com/spring-security-oauth-jwt ************************************************** ...
- oracle 使用occi方式插入数据时中文乱码
这个是由于数据库的编码格式和我们输入的编码格式不一致导致的. 我们使用c++插入数据时数据库的中文显示??(即乱码),但同样的数据使用navicat进行插入却显示正常. 因此,问题并不是处在服务器端的 ...
- vue 单元测试
单元测试 配置和工具 任何兼容基于模块的构建系统都可以正常使用,但如果你需要一个具体的建议,可以使用Karma进行自动化测试.它有很多社区版的插件,包括对webpack和browserify的支持. ...
- php代码检查
最近写php,几个同事都是没写过c的,经常写的变量没有定义,而php没有编译,错误无法发现. 我们现在用的是NetBeans,好在其提供了语法检测,如下图,让编辑器强制显示我错误
- JsonNode、JsonObject常用方法
最近项目中要用json,闲暇时间,对json进行下总结. 1.JsonNode 项目中用到的jar包 import com.fasterxml.jackson.core.JsonParseExce ...
- ftp安装和虚拟用户创建
安装 1.安装 sudo apt-get install vsftpd 2 查看安装结果 安装完毕,检查vsftpd进程是否已启动,可以查看进程或者查看监听端口 ps -eaf|grep vsftpd ...
- multimap 小例子
场景: 按DDX值倒序取前十的板块代码,用 map<float, string, greater<float> > mapBKDDX; 存储时,相同DDX值的板块只能存储第一个 ...
- 一个线程可以拿到多个锁标记,一个对象最多只能将monitor给一个线程
当用Synchronized修饰某个方法的时候,表示该方法都对当前对象加锁. 给方法加Synchronized和用Synchronized修饰对象的效果是一致的. 一个线程可以拿到多个锁标记,一个对象 ...
- java---多线程及线程的概念
如果对什么是线程.什么是进程仍存有疑惑,请先Google之,因为这两个概念不在本文的范围之内. 用多线程只有一个目的,那就是更好的利用cpu的资源,因为所有的多线程代码都可以用单线程来实现.说这个话其 ...
- php -- php的事务处理
MYSQL的事务处理主要有两种方法. 1.用begin,rollback,commit来实现 begin 开始一个事务 rollback 事务回滚 commit 事务确认 2.直接用set来改变mys ...