kafka分区副本分配算法解析
最近在看《深入理解Kafka:核心设计与实践原理 》来系统学习kafka,第一个令我想深入了解的就是这个知识点:分区副本分配算法
算法功能
各个分区副本 均衡分配到 不同的broker节点 中,既避免因为某个broker宕机导致整个分区不可用,也实现了各个broker节点的负载均衡
broker节点可以视为 不同的服务器
引入
我们看看分区副本分配算法的效果
有三个kafka broker节点
- 步骤
- 创建一个 有 4 个分区,每个分区有2个副本的 topic
topic-hello
,(可视为有4*2 = 8
个分区副本) - 查看
topic-hello
各个分区分布情况
- 创建一个 有 4 个分区,每个分区有2个副本的 topic
➜ bin ./kafka-topics.sh --bootstrap-server 172.16.17.188:9092 --create --topic topic-hello --partitions 4 --replication-factor 2
Created topic topic-hello.
➜ bin ./kafka-topics.sh --bootstrap-server 172.16.17.188:9092 --describe --topic topic-hello
Topic: topic-hello PartitionCount: 4 ReplicationFactor: 2 Configs: segment.bytes=1073741824
Topic: topic-hello Partition: 0 Leader: 2 Replicas: 2,1 Isr: 2,1
Topic: topic-hello Partition: 1 Leader: 1 Replicas: 1,0 Isr: 1,0
Topic: topic-hello Partition: 2 Leader: 0 Replicas: 0,2 Isr: 0,2
Topic: topic-hello Partition: 3 Leader: 2 Replicas: 2,0 Isr: 2,0
如上图我们可以得到,各个分区副本分在哪个broker里面:
## 描述A
分区0 分在 [2,1]
分区1 分在 [1,0]
分区2 分在 [0,2]
分区3 分在 [2,0]
所以,各个broker拥有的分区情况如下:
## 描述B
broker-0 有分区 [1,2,3]
broker-1 有分区 [0,1]
broker-2 有分区 [1,2,3]
结合上述的算法功能,如果人工来分配在三个broker里分配 8 个分区副本,人工也会这样分配成这样的3+3+2=8
的模式,因为这样对负载也相对均衡;各个副本也分布在不同的 broker节点中,也就更为保险。
其实上述两种描述,描述的是算法的不同方面:
- 描述A: 算法实现的 保险功能
- 描述B:算法实现的 负载均衡 功能
实现方法
原理分析
以下只考虑
fixedStartIndex=-1
,startPartitionId=-1
的情况
前提
- 副本因子 <= broker数量
- 知道要总共要处理多少个资源(很多负载均衡算法就是不知道这个)
关键点
- 随机产生一个副本距离
replicaGap
——为了多次通过此算法分配时,避免固定的方案,这样会导致负载不均衡 - 选出第一个分区所属的broker
int firstReplicaIndex = (currentPartitionId + startIndex) % brokerList.size();
因为startIndex
是随机的,因此firstReplicaIndex
也是随机的随机是为了多次通过此算法分配时,不能每一次都从同一个 broker 开始,对其不公平
源码分析
- 主程序
/**
* 分区副本分配 主要内容
* @param partitionCount 分区总数
* @param replicaFactor 分区副本因子,也就是每个分区有多少个副本
* @param brokerList broker节点列表,这里为了好区分,已经用名字代替,实际算法中是一个 List<Integer>
* @param fixedStartIndex 默认为-1,此时随机产生一个在 [0.brokerList.size()-1]的随机数 n,那么 broker-n 就是容纳 第一个分区的 第一个broker
* @param startPartitionId 默认为 -1,-1意味着第一个处理的分区是 分区0 ,否则 第一个处理的分区是 分区startPartitionId
* @return key 为分区id,value 为其分区副本所在的brokerList
*/
private static Map<Integer, List<String>> allocatePartitionReplica(int partitionCount, int replicaFactor, List<String> brokerList,
int fi