記一次 Kafka 集羣線上擴容

前段時間收到某個 Kafka 集羣的生產客戶端反饋發送消息耗時很高,於是花了一段時間去排查這個問題,最後該集羣進行擴容,由於某些主題的當前數據量實在太大,在對這些主題遷移過程中話費了很長一段時間,不過這個過程還算順利,因爲在遷移過程中也做足了各方面的調研,包括分區重平衡過程中對客戶端的影響,以及對整個集羣的性能影響等,特此將這個過程總結一下,也爲雙十一打了一劑強心劑。

排查問題與分析

接到用戶的反饋後,我用腳本測試了一遍,並對比了另外一個正常的 Kafka 集羣,發現耗時確實很高,接下來

經過排查,發現有客戶端在頻繁斷開與集羣節點的連接,發現日誌頻繁打印如下內容:

Attempting to send response via channel for which there is no open connection, connection id xxx(kafka.network.Processor)

定位到源碼位置:

kafka.network.Processor#sendResponse:

看源碼註釋,是遠程連接關閉了或者空閒時間太長了的意思,找到具體客戶端負責人,經詢問後,這是大數據 Spark 集羣的節點。

從以上日誌看出,Spark 集羣的某個消費組 OrderDeliveryTypeCnt,竟然發生了近 4 萬次重平衡操作,這顯然就是一個不正常的事件,Kafka 消費組發生重平衡的條件有以下幾個:

  1. 消費組成員發生變更,有新消費者加入或者離開,或者有消費者崩潰;
  2. 消費組訂閱的主題數量發生變更;
  3. 消費組訂閱的分區數發生變更。

很顯然第 2、3 點都沒有發生,那麼可以斷定,這是 Spark集羣節點頻繁斷開與kafka的連接導致消費組成員發生變更,導致消費組發生重平滑。

那爲什麼 Spark 集羣會產生頻繁斷開重連呢?

查看 Spark 集羣用的 Kafka 版本還是 0.10.1.1 版本,而 Kafka 集羣的版本爲 2.2.1,一開始以爲是版本兼容問題,接着數據智能部的小夥伴將 Spark 集羣連接到某個版本爲 0.11.1.1 的 Kafka 集羣,使用 8 個 Spark 任務消費進行消費,同樣發現了連接斷開的問題。說明此問題是由於 Spark 內部消費 Kafka 機制導致的,和 kafka 版本關係不大。

經過幾番跟大數據的人員討論,這個頻繁重平衡貌似是 Spark 2.3 版本內部機制導致的,Spark 2.4 版本沒有這個問題存在。

由於這個頻繁斷開重連,並不是開發人員開發過程中導致的,考慮到雙十一臨近,不能貿然升級改動項目,那麼現在最好的方案就是對集羣進行水平擴展,增加集羣的負載能力,並對專門的主題進行分區重分配。

分區重分配方案的分析

目前集羣一共有 6 個節點,擴容以 50% 爲基準,那麼需要在準備 3 個節點,在運維準備好機器並且將其加入到集羣中後,接下來就要準備對主題進行分區重分配的策略文件了。

在執行分區重分配的過程中,對集羣的影響主要有兩點:

  1. 分區重分配主要是對主題數據進行 Broker 間的遷移,因此會佔用集羣的帶寬資源;
  2. 分區重分配會改變分區 Leader 所在的 Broker,因此會影響客戶端。

針對以上兩點,第 1 點可以在晚間進行(太苦逼了,記得有個主題數據遷移進行了將近5小時),針對第二點,我想到了兩個方案:

  1. 整個分配方案分成兩個步驟:1)手動生成分配方案,對原有的分區 Leader 位置不改變,只對副本進行分區重分配;2)等待數據遷移完成後,再手動更改分區分配方案,目的是均衡 Leader。
  2. 直接用 Kafka 提供的 API 生成 分區重分配方案,直接執行分區重分配。

第一個方案理論上是對客戶端影響最小的,把整個分配方案分成了兩個步驟,也就是將對集羣的帶寬資源與客戶端的影響分開了,對過程可控性更高了,但問題來了,集羣中的某些主題,有 64 個分區,副本因子爲 3,副本一共有 192 個,你需要保持原有分區 Leader 位置不變的情況下,去手動均衡其餘副本,這個考驗難度真的太大了,稍微有一點偏差,就會造成副本不均衡。

因此我特意去看了分區重分配的源碼,並對其過程進行了進一步分析,發現分配重分配的步驟是將分區原有的副本與新分配的副本的集合,組成一個分區副本集合,新分配的副本努力追上 Leader 的位移,最終加入 ISR,待全部副本都加入 ISR 之後,就會進行分區 Leader 選舉,選舉完後就會將原有的副本刪除,具體細節我會單獨寫一篇文章。

根據以上重分配的步驟,意味着在數據進行過程中不會發生客戶端阻塞,因爲期間 Leader 並沒有發生變更,在數據遷移完成進行 Leader 選舉時纔會,但影響不大,針對這點影響我特意用腳本測試了一下:

可以發現,在發送過程中,如果 Leader 發生了變更,生產者會及時拉取最新的元數據,並重新進行消息發送。

針對以上的分析與測試,我們決定採取第二種方案,具體步驟如下:

  1. 對每個主題生成分配分區分配策略:執行時間段(10:00-17:00),並對分配策略進行檢查,並保存執行的 topic1_partition_reassignment.json 文件,並把原來的方案保存到topic1_partition_reassignment_rollback.json 文件中,以備後續的 rollback 操作;
  2. 執行分配策略:執行時間段(00:30-02:30),準備好的 topic1_partition_reassignment.json 文件,執行完再驗證並查看副本分配情況,每執行一個分配策略都要查看 ISR 收縮擴張狀況、消息流轉狀況,確定沒問題後再執行下一個分配策略;
  3. 由於集羣 broker 端的參數 auto.leader.rebalance.enable=true,因此會自動執行 Preferred Leader 選舉,默認時間間隔爲 300 秒,期間需要觀察 Preferred Leader 選舉狀況。

分區重分配

對於新增的 Broker,Kafka 是不會自動地分配已有主題的負載,即不會將主題的分區分配到新增的 Broker,但我們可以通過 Kafka 提供的 API 對主題分區進行重分配操作,具體操作如下:

  1. 生成需要執行分區重分配的主題列表 json 文件:
echo '{"version":1,"topics":[{"topic":"sjzn_spark_binlog_order_topic"}]}' > sjzn_spark_binlog_order_topic.json
  1. 生成主題的分配方案:
bin/kafka-reassign-partitions.sh  --zookeeper  --zookeeper xxx.xxx.xx.xxx:2181,xxx.xxx.xx.xxx:2181,xxx.xxx.xx.xxx:2181 --topics-to-move-json-file sjzn_spark_binlog_order_topic.json --broker-list "0,1,2,3,4,5,6,7,8" --generate

由於主題的有64個分區,每個分區3個副本,生成的分配數據還是挺大的,這裏就不一一貼出來了

  1. 將分配方案保存到一個 json 文件中:
echo '{"version":1,"partitions":[{"topic":"sjzn_spark_binlog_order_topic","partition":59,"replicas":[4,8,0],"log_dirs":["any","any","any"]} ......' > sjzn_spark_binlog_order_topic_reassignment.json
  1. 執行分區重分配:
 bin/kafka-reassign-partitions.sh   --zookeeper xxx.xxx.xx.xxx:2181,xxx.xxx.xx.xxx:2181,xxx.xxx.xx.xxx:2181 --reassignment-json-file sjzn_spark_binlog_order_topic_reassignment.json --execute
  1. 驗證分區重分配是否執行成功:
bin/kafka-reassign-partitions.sh  --zookeeper xxx.xxx.xx.xxx:2181,xxx.xxx.xx.xxx:2181,xxx.xxx.xx.xxx:2181 --reassignment-json-file sjzn_spark_order_unique_topic_resign.json --verify

由於該主題存在的數據量特別大,整個重分配過程需要維持了好幾個小時:

在它進行數據遷移過程中,我特意去 kafka-manage 控制檯觀察了各分區數據的變動情況:

從控制檯可看出,各分區的副本數目基本都增加了,這也印證了分區當前的副本數等於原有的副本加上新分配的副本的集合,新分配的副本集合目前還沒追上 Leader 的位移,因此沒有加入 ISR 列表。

有沒有注意到一點,此時各分區的 Leader 都不在 Preferred Leader 中,因此後續等待新分配的副本追上 ISR 後,會進行新一輪的 Preferred Leader 選舉,選舉的細節實現我會單獨寫一篇文章去分析,敬請期待。

過一段時間後,發現位移已經改變了:

從這點也印證了在分區重分配過程中,只要 Leader 沒有發生變更,客戶端是可以持續發送消息給分區 Leader 的。

從上圖可看出,新分配的副本追上 Leader 的位移後,就會加入 ISR 列表中。

現在去看看集羣帶寬負載情況:

從上圖中可看出,在遷移過程中,新分配的副本不斷地從 Leader 拉取數據,佔用了集羣帶寬。

主題各分區重分配完成後的副本情況:

從以上圖中可看出,各分區的新分配的副本都已經全部在 ISR 列表中了,並且將舊分配的副本刪除,經過 Preferred Leader 選舉之後,各分區新分配副本的 Preferred Leader 大多數成爲了該分區 leader。

更多精彩文章請關注作者維護的公衆號「後端進階」,這是一個專注後端相關技術的公衆號。
關注公衆號並回復「後端」免費領取後端相關電子書籍。
歡迎分享,轉載請保留出處。

公衆號「後端進階」,專注後端技術分享!

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