replica.alter.log.dirs.io.max.bytes.per.second
broker内部目录之间迁移数据流量限制功能,限制数据拷贝从一个目录到另外一个目录带宽上限
leader.replication.throttled.replicas
: leader端的副本限流
follower.replication.throttled.replicas
: follower端的副本限流
就算你不记得也没有关系,那我们今天来好好讲一讲这一块的内容
首先我们看下判断Follower是否应该被限流的判断逻辑
/**
- 为避免ISR抖动,我们仅在跟随者上的副本处于限制副本列表中、超出配额、且副本不同步时候对其进行限流
*/
private def shouldFollowerThrottle(quota: ReplicaQuota, fetchState: PartitionFetchState, topicPartition: TopicPartition): Boolean = {
!fetchState.isReplicaInSync && quota.isThrottled(topicPartition) && quota.isQuotaExceeded
}
被限流的条件如下
-
副本不在ISR列表中
-
该副本在限流配置列表中
-
超过限流阈值了
这里我们主要分析一下,如何才能在限流配置列表中
private def shouldFollowerThrottle(quota: ReplicaQuota, fetchState: PartitionFetchState, topicPartition: TopicPartition): Boolean = {
!fetchState.isReplicaInSync && quota.isThrottled(topicPartition) && quota.isQuotaExceeded
}
override def isThrottled(topicPartition: TopicPartition): Boolean = {
val partitions = throttledPartitions.get(topicPartition.topic)
if (partitions != null)
(partitions eq AllReplicas) || partitions.contains(topicPartition.partition)
else false
}
上面的ConcurrentHashMap throttledPartitions
就是所有需要被限流的副本列表,那么是在哪里被赋值的呢?
ConfigHandler#processConfigChanges
def processConfigChanges(topic: String, topicConfig: Properties): Unit = {
// Validate the configurations.
val configNamesToExclude = excludedConfigs(topic, topicConfig)
updateLogConfig(topic, topicConfig, configNamesToExclude)
def updateThrottledList(prop: String, quotaManager: ReplicationQuotaManager) = {
if (topicConfig.containsKey(prop) && topicConfig.getProperty(prop).length > 0) {
val partitions = parseThrottledPartitions(topicConfig, kafkaConfig.brokerId, prop)
println(prop+“本机:”+kafkaConfig.brokerId+" Topic"+topic+“; 需要限流的分区有:”+partitions.mkString(" "))
quotaManager.markThrottled(topic, partitions)
debug(s"Setting $prop on broker ${kafkaConfig.brokerId} for topic: $topic and partitions $partitions")
} else {
quotaManager.removeThrottle(topic)
debug(s"Removing $prop from broker ${kafkaConfig.brokerId} for topic $topic")
}
}
// 这里,更新限流副本
updateThrottledList(LogConfig.LeaderReplicationThrottledReplicasProp, quotas.leader)
updateThrottledList(LogConfig.FollowerReplicationThrottledReplicasProp, quotas.follower)
if (Try(topicConfig.getProperty(KafkaConfig.UncleanLeaderElectionEnableProp).toBoolean).getOrElse(false)) {
kafkaController.enableTopicUncleanLeaderElection(topic)
}
}
这段代码是在修改了动态配置中的topic节点的时候会被触发的,关于动态配置可以看我之前的文章 [Kafka中的动态配置源码分析
]( )
这里的代码简单描述下
-
检查 动态配置 中是否存在配置
leader.replication.throttled.replicas
和follower.replication.throttled.replicas
-
如果存在,则需要解析一下配置Value,解析得到的Value就是需要被限流的副本列表,将它写到内存中。
-
如果不存在,则需要把当前的限流副本列表清空。
具体的解析逻辑如下
ConfigHandler#parseThrottledPartitions
def parseThrottledPartitions(topicConfig: Properties, brokerId: Int, prop: String): Seq[Int] = {
val configValue = topicConfig.get(prop).toString.trim
ThrottledReplicaListValidator.ensureValidString(prop, configValue)
configValue match {
case “” => Seq()
case “*” => AllReplicas
case _ => configValue.trim
.split(“,”)
.map(_.split(“:”))
.filter(_ (1).toInt == brokerId) //Filter this replica
.map(_ (0).toInt).toSeq //convert to list of partition ids
}
}
-
如果为空就没有需要限流的
-
如果是
*
表示所有副本都需要限流 -
配置值的格式为:
分区号:副本所在BrokerId,分区号:副本所在BrokerId
并过滤一下副本所在BrokerId
=自己的BrokerId
例如: 1:102,2:0,3:0
在当前 BrokerID=102 的集群上最终解析出来需要限流的副本为 1 , 只需要解析跟自己相关的副本就行了。
具体限流需要限制在多少
上面只是讲了怎么将分区副本设置为需要限流, 但是并没有设置限流多少鸭!就算上面你设置了,这里没有设置限流多少,那么默认的限流值就是 Long.MAXVALUE
约等于没有限流。
那么如何设置限流的流速呢?
请看下面代码, 这个是 在修改了Broker的动态配置之后就会调用的方法, 关于动态配置可以看我之前的文章 [Kafka中的动态配置源码分析
]( )
BrokerConfigHandler#processConfigChanges
class BrokerConfigHandler(private val brokerConfig: KafkaConfig,
private val quotaManagers: QuotaManagers) extends ConfigHandler with Logging {
def processConfigChanges(brokerId: String, properties: Properties): Unit = {
def getOrDefault(prop: String): Long = {
if (properties.containsKey(prop))
properties.getProperty(prop).toLong
else
DefaultReplicationThrottledRate
}
if (brokerId == C