深入理解kafka核心設計跟實踐原理之分區的管理

本文主要討論kafka 分區的初始分配、重分配(本文不討論consumer情況)

首先,從kafka的底層實現來說,主題與分區都是邏輯上的概念,分區可以有一至多個副本,每個副本對於一個日誌文件(物理層面)。

要研究kafka分區的初始分配、重分配需知曉以下概念。

1、AR(Assigned Replicas):分區中的所有副本

2、ISR(In-Sync Replicas)與leader副本保持一定程度同步的副本(包括leader副本)

3、OSR(Out-of-Sync Replicas)與leade副本同步滯後過多的副本。

由此可見AR=ISR+OSR,一般情況下AR=ISR,OSR爲空,kafka集羣的一個broker中最多隻能有一個它的副本,我們可以將leader副本所在的broker節點叫做分區的leader節點,而follower副本所在的broker節點叫做分區的follower節點。

接下來開始本文的重點討論

一、kafka 分區的初始分配

這裏的分區分配☞爲集羣創建指定主題時的分區副本分配方案,即在哪個broker中創建哪些分區的副本。

在創建主題時,如果使用了replica-assignment參數,那麼就按照指定的方案來進行分區副本的創建;如果沒有則按照kakfa內部實現計算分配。內部分配分2種策略:1、未指定機架 2、指定機架 ,如果集羣中的所有broker節點都沒有配置broker.rack參數,則採用disable-rack-aware參數來創建主題,否則採用指定機架創建主題。

1、未指定機架策略:均勻分配所有副本到所有的broker上

private def assignReplicasToBrokersRackUnaware(
nPartitions: Int, //分區數
replicationFactor: Int, //副本因子
brokerList: Seq[Int], //集羣中的broker列表
fixedStartIndex: Int , //起始索引,即第一個副本分配的位置,默認值爲-1
startPatitionId: Int  //起始分區編號,默認值爲-1
):
Map[Int,Seq[Int]] = {
	val ret = mutable.Map[Int,Seq[Int]]()  // 保存分配結果的集合
	val brokerArray = brokerList.toArray  //brokerId的列表
	//如果起始索引fixedStartIndex小於0,則根據broker列表的長度隨機生成一個,以此來保證是有效的brokerId
	val startIndex = if(fixedStartIndex>0) fixedStartIndex
	   else rand.nextInt(brokerArray.length)
	//確保起始分區號不小於0
	var currentPartitionId = math.max(0,startPatitionId)
	//指定了副本的間隔,目的是爲了更均勻的將副本分配到不同的broker上
	var nextReplicaShift = if(fixedStartIndex>=0) fixedStartIndex
	   else rand.nextInt(brokerArray.length)
	//輪詢所有分區,將每個分區的副本分配到不同的broker上
	for(_<- 0 until nPartitions){
		if(currentPartitionId>0 && currentPartitionId%brokerArray.length == 0)
			nextReplicaShift+=1
		//從broker-list中選定一個隨機的位置,從這個位置開始,將每一個partition的第一個replica依次賦予brokerList中的broker.
		val firstReplicaIndex = (currentPartitionId+startIndex)%brokerArray.length   
		val replicaBuffer = mutable.ArrayBuffer(brokerArray(firstReplicaIndex))
		//保存該分區所有副本分配的broker集合
		for(j<- 0 until replicationFactor - 1)
			replicaBuffer+=brokerArray(replicaIndex(firstReplicaIndex,nextReplicaShift,j,brokerArray.length))//爲其餘的副本分配broker
		//保存該分區的所有副本的分配信息
		ret.put(currentPartitionId,replicaBuffer)
		//繼續爲下一個分區分配副本
		currentPartitionId+=1
	}
	ret
}
//1,0,1,3  當分配好了第一個replica之後,剩下的replica以第一個replica所在的broker爲基準,依次賦予之後的broker
private def replicaIndex(firstReplicaIndex: Int, secondReplicaShift: Int, replicaIndex: Int, nBrokers: Int): Int = {
    val shift = 1 + (secondReplicaShift + replicaIndex) % (nBrokers - 1)
    (firstReplicaIndex + shift) % nBrokers
}

2、指定機架分配分區

如機架與broker節點的對照關係如下:

rack1:0,1,2

rack2:3,4,5

rack3:6,7,8

未指定機架的brokeArray爲[0,1,2,3,4,5,6,7,8],而指定機架的brokerArray爲[0,3,6,1,4,7,2,5,8],循環爲每個分區分配副本,儘量保證分區在機架中均勻分佈,這裏不是簡單的將這個broker添加到當前分區的副本列表中,還要經過一層篩選,滿足以下任意一個條件的broker不能被添加到當前分區的副本列表中:1、如果此broker所在的機架中已經存在一個broker擁有該分區的副本,並且還有其他機架中沒有任何一個broker擁有該分區的副本 2、如果此broker中已經擁有了該分區的副本,並且還有其他broker中沒有該分區的副本

二、kafka分區的重分配

1、優先副本的選取

分區使用多副本機制來提升可靠性,leader副本負責讀寫,follower只負責在內部進行消息的同步,一旦leader節點掛掉,kafka需要從follower副本中挑選一個作爲leader副本。如:原先3個broker,3個分區,當其中一個broker掛掉之後,剩下的2個broker承擔的分區數變成2、1,如此一來,均衡的負載變得不均衡,爲了有效的治理負載均衡問題,kakfa引入了優先副本的概念,所謂的優先副本是指AR集合列表的第一個副本,理想情況下,優先副本就是該分區的leader副本。kafka要確保所有的主題的優先副本在kafka中均勻分佈,這樣就保證了整個kafka集羣的負載均衡。

於此:有個參數需要了解:

auto.leader.rebalance.enable:此參數的默認值爲true,如果一個分區有3個副本,且3個副本的優先級爲0,1,2,根據副本優先概念,0會作爲leader,當0掛掉後,會啓動1作爲leader,當0恢復後,會重新啓用0作爲leader,這就是分區自動平衡,保證負載均衡,執行週期由參數leader.imbalance.check.interval.seconds控制,默認5分鐘,分區自動平衡會造成客戶端的堵塞,生產環境不建議開啓,可手動執行分區平衡。

2、分區重分配

當一個節點的分區副本不可用時,kafka不會自動將這些失效的分區副本遷移到集羣剩餘的可用的broker節點上,會影響負載均衡以及服務的可用性、可靠性,當集羣中新增一個節點時,只有新創建的主題分區纔有可能分配到這個節點上,之前的主題分區不會自動分配到這個新的節點上,也會影響其負載均衡

爲了解決上訴問題,引入分區重分配方案,kafka提供了kafka-reassign-partitions.sh腳本來執行分區重分配的工作(集羣擴容、節點失效場具下工作)                主要分爲以下3個步驟:首先創建一個包含主題清單的JSON文件,其次根據主題清單和broker節點清單生成一份重分配方案,最後根據這份方案執行具體的重分配動作。

具體操作,後續本地運行一遍後補上

參考:《深入理解kafka核心設計與實踐原理》

 

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章