深入淺出系列之 -- kafka分區分配策略

生產者的分區分配

對於用戶而言,當調用send方法發送消息之後,消息就自然而然的發送到了broker中。其實在這一過程中,有可能還要經過攔截器序列化器分區器(Partitioner)的一系列作用之後才能被真正地發往broker。

producer.send(record);

消息在發往broker之前是需要確定它所發往的分區的,如果消息ProducerRecord中指定了partition字段,那麼就不需要分區器的作用,因爲partition代表的就是所要發往的分區號。但是如果消息ProducerRecord中沒有指定partition字段,那麼就需要依賴分區器根據key這個字段來計算partition的值分區器的作用就是爲消息分配分區

Kafka中提供的默認分區器是DefaultPartitioner,它實現了Partitioner接口(用戶可以實現這個接口來自定義分區器),其中的partition方法就是用來實現具體的分區分配邏輯:

public int partition(String topic, Object key, byte[] keyBytes,
                     Object value, byte[] valueBytes, Cluster cluster);

默認情況下,如果消息的key不爲null,那麼默認的分區器會對key進行哈希(採用MurmurHash2算法,具備高運算性能及低碰撞率),最終根據得到的哈希值來計算分區號,擁有相同key的消息會被寫入同一個分區。如果key爲null,那麼消息將會以輪詢的方式發往主題內的各個可用分區。

注意:如果key不爲null,那麼計算得到的分區號會是所有分區中的任意一個;如果key爲null並且有可用分區,那麼計算得到的分區號僅爲可用分區中的任意一個,注意兩者之間的差別。

消費者的分區分配

用過Kafka的同學用過都知道,每個主題一般會有很多個分區。爲了使得我們能夠及時消費消息,我們也可能會啓動多個消費者去消費,而每個消費者又會啓動一個或多個溪流去分別消費Topic裏面的數據。我們又知道,Kafka存在Consumer Group的概念,也就是group.id一樣的Consumer,這些Consumer屬於同一個Consumer Group,組內的所有消費者協調在一起來消費訂閱主題(訂閱主題)的所有分區(分區)。當然,每個分區只能由同一個消費組內的一個消費者來消費。那麼問題來了,同一個消費者羣體裏面的消費者是如何知道該消費哪些分區裏面的數據呢?

如上圖,Consumer1爲啥消費的是Partition0和Partition2,而不是Partition0和Partition3?這就涉及到Kafka內部分區分配策略(Partition Assignment Strategy)了。

在Kafka內部存在兩種默認的分區分配策略:RangeRoundRobin。當以下事件發生時,Kafka將會進行一次分區分配:

  • 同一個Consumer Group內新增消費者
  • 消費者離開當前所屬的Consumer Group,包括關閉或崩潰
  • 訂閱的主題新增分區

將分區的所有權從一個消費者移到另一個消費者稱爲重新平衡(再平衡),如何再平衡就涉及到本文提到的分區分配策略。下面我們將詳細介紹Kafka內置的兩種分區分配策略。本文假設我們有個名爲T1的主題,其包含了10個分區,然後我們有兩個消費者(C1,C2)來消費這10個分區裏面的數據,而且C1的num.streams = 1,C2的num.streams = 2。

範圍策略

一系列策略是對每個主題而言的,首先對同一個主題裏面的分區按照序號進行排序,並對消費者按照字母順序進行排序。在我們的例子裏面,排完序的分區將會是0,1,2,3,4,5,6,7,8,9;消費者線程排序將會是C1-0,C2-0,C2-1。然後將partitions的個數除於消費者線程的總數來決定每個消費者線程消費幾個分區。如果除不盡,那麼前面幾個消費者線程將會多消費一個分區。在我們的例子裏面,我們有10個分區,3個消費者線程,10/3 = 3,而且除除不盡,那麼消費者線程C1-0將會多消費一個分區,所以最後分區分配的結果看起來是這樣的:

C1-0 將消費 0, 1, 2, 3 分區
C2-0 將消費 4, 5, 6 分區
C2-1 將消費 7, 8, 9 分區

假如我們有11個分區,那麼最後分區分配的結果看起來是這樣的:

C1-0 將消費 0, 1, 2, 3 分區
C2-0 將消費 4, 5, 6, 7 分區
C2-1 將消費 8, 9, 10 分區

假如我們有2個主題(T1和T2),分別有10個分區,那麼最後分區分配的結果看起來是這樣的:

C1-0 將消費 T1主題的 0, 1, 2, 3 分區以及 T2主題的 0, 1, 2, 3分區
C2-0 將消費 T1主題的 4, 5, 6 分區以及 T2主題的 4, 5, 6分區
C2-1 將消費 T1主題的 7, 8, 9 分區以及 T2主題的 7, 8, 9分區

可以看出,C1-0消費者線程比其他消費者線程多消費了2個分區,這就是範圍策略的一個很明顯的弊端。

RoundRobin戰略

使用輪轉策略有兩個前提條件必須滿足:

  • 同一個Consumer Group裏面的所有消費者的num.streams必須相等;

  • 每個消費者訂閱的主題必須相同。

所以這裏假設前面提到的2個消費者的num.streams = 2.RoundRobin策略的工作原理:將所有主題的分區組成TopicAndPartition列表,然後對TopicAndPartition列表按照hashCode進行排序,這裏文字可能說不清,看下面的代碼應該會明白:

val allTopicPartitions = ctx.partitionsForTopic.flatMap { case(topic, partitions) =>
  info("Consumer %s rebalancing the following partitions for topic %s: %s"
       .format(ctx.consumerId, topic, partitions))
  partitions.map(partition => {
    TopicAndPartition(topic, partition)
  })
}.toSeq.sortWith((topicPartition1, topicPartition2) => {
  /*
   * Randomize the order by taking the hashcode to reduce the likelihood of all partitions of a given topic ending
   * up on one consumer (if it has a high enough stream count).
   */
  topicPartition1.toString.hashCode < topicPartition2.toString.hashCode
})

最後按照循環賽風格將分區分別分配給不同的消費者線程。

在我們的例子裏面,加入按照hashCode排序完的主題 - 分區組依次爲T1-5,T1-3,T1-0,T1-8,T1-2,T1-1,T1-4,T1-7, T1-6,T1-9,我們的消費者線程排序爲C1-0,C1-1,C2-0,C2-1,最後分區分配的結果爲:

C1-0 將消費 T1-5, T1-2, T1-6 分區;
C1-1 將消費 T1-3, T1-1, T1-9 分區;
C2-0 將消費 T1-0, T1-4 分區;
C2-1 將消費 T1-8, T1-7 分區;

多個主題的分區分配和單個主題類似,這裏就不在介紹了。

根據上面的詳細介紹相信大家已經對Kafka的分區策略原理很清楚了。不過遺憾的是,目前我們還不能自定義分區分配策略,只能通過partition.assignment.strategy參數選擇range或roundrobin.partition。 assignment.strategy參數默認的值是範圍。

 

broker端的分區分配

    生產者的分區分配是指爲每條消息指定其所要發往的分區,消費者中的分區分配是指爲消費者指定其可以消費消息的分區,而這裏的分區分配是指爲集羣制定創建主題時的分區副本分配方案,即在哪個broker中創建哪些分區的副本。分區分配是否均衡會影響到Kafka整體的負載均衡,具體還會牽涉到優先副本等概念。

在創建主題時,如果使用了replica-assignment參數,那麼就按照指定的方案來進行分區副本的創建;如果沒有使用replica-assignment參數,那麼就需要按照內部的邏輯來計算分配方案了。使用kafka-topics.sh腳本創建主題時的內部分配邏輯按照機架信息劃分成兩種策略:未指定機架信息和指定機架信息。如果集羣中所有的broker節點都沒有配置broker.rack參數,或者使用disable-rack-aware參數來創建主題,那麼採用的就是未指定機架信息的分配策略,否則採用的就是指定機架信息的分配策略。

 

------------------------------------------------------------------------------------------------------------------------------------------------------------------------

      考慮一千次,不如去做一次;猶豫一萬次,不如實踐一次;華麗的跌倒,勝過無謂的彷徨,將來的你,一定會感謝現在奮鬥的你。歡迎大家加入大數據交流羣:725967421     一起交流,一起進步!!

------------------------------------------------------------------------------------------------------------------------------------------------------------------------
 

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