前段時間收到某個 Kafka 集羣的生產客戶端反饋發送消息耗時很高,因而花了一段時間去排查這個問題,最後該集羣進行擴容,因爲某些主題的當前數據量實在太大,在對這些主題遷移過程當中話費了很長一段時間,不過這個過程還算順利,由於在遷移過程當中也作足了各方面的調研,包括分區重平衡過程當中對客戶端的影響,以及對整個集羣的性能影響等,特此將這個過程總結一下,也爲雙十一打了一劑強心劑。json
接到用戶的反饋後,我用腳本測試了一遍,並對比了另一個正常的 Kafka 集羣,發現耗時確實很高,接下來後端
通過排查,發現有客戶端在頻繁斷開與集羣節點的鏈接,發現日誌頻繁打印以下內容:bash
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 消費組發生重平衡的條件有如下幾個:大數據
很顯然第 二、3 點都沒有發生,那麼能夠判定,這是 Spark集羣節點頻繁斷開與kafka的鏈接致使消費組成員發生變動,致使消費組發生重平滑。spa
那爲何 Spark 集羣會產生頻繁斷開重連呢?.net
查看 Spark 集羣用的 Kafka 版本仍是 0.10.1.1 版本,而 Kafka 集羣的版本爲 2.2.1,一開始覺得是版本兼容問題,接着數據智能部的小夥伴將 Spark 集羣鏈接到某個版本爲 0.11.1.1 的 Kafka 集羣,使用 8 個 Spark 任務消費進行消費,一樣發現了鏈接斷開的問題。說明此問題是因爲 Spark 內部消費 Kafka 機制致使的,和 kafka 版本關係不大。3d
通過幾番跟大數據的人員討論,這個頻繁重平衡貌似是 Spark 2.3 版本內部機制致使的,Spark 2.4 版本沒有這個問題存在。
因爲這個頻繁斷開重連,並非開發人員開發過程當中致使的,考慮到雙十一臨近,不能貿然升級改動項目,那麼如今最好的方案就是對集羣進行水平擴展,增長集羣的負載能力,並對專門的主題進行分區重分配。
目前集羣一共有 6 個節點,擴容以 50% 爲基準,那麼須要在準備 3 個節點,在運維準備好機器而且將其加入到集羣中後,接下來就要準備對主題進行分區重分配的策略文件了。
在執行分區重分配的過程當中,對集羣的影響主要有兩點:
針對以上兩點,第 1 點能夠在晚間進行(太苦逼了,記得有個主題數據遷移進行了將近5小時),針對第二點,我想到了兩個方案:
第一個方案理論上是對客戶端影響最小的,把整個分配方案分紅了兩個步驟,也就是將對集羣的帶寬資源與客戶端的影響分開了,對過程可控性更高了,但問題來了,集羣中的某些主題,有 64 個分區,副本因子爲 3,副本一共有 192 個,你須要保持原有分區 Leader 位置不變的狀況下,去手動均衡其他副本,這個考驗難度真的太大了,稍微有一點誤差,就會形成副本不均衡。
所以我特地去看了分區重分配的源碼,並對其過程進行了進一步分析,發現分配重分配的步驟是將分區原有的副本與新分配的副本的集合,組成一個分區副本集合,新分配的副本努力追上 Leader 的位移,最終加入 ISR,待所有副本都加入 ISR 以後,就會進行分區 Leader 選舉,選舉完後就會將原有的副本刪除,具體細節我會單獨寫一篇文章。
根據以上重分配的步驟,意味着在數據進行過程當中不會發生客戶端阻塞,由於期間 Leader 並無發生變動,在數據遷移完成進行 Leader 選舉時纔會,但影響不大,針對這點影響我特地用腳本測試了一下:
能夠發現,在發送過程當中,若是 Leader 發生了變動,生產者會及時拉取最新的元數據,並從新進行消息發送。
針對以上的分析與測試,咱們決定採起第二種方案,具體步驟以下:
auto.leader.rebalance.enable=true
,所以會自動執行 Preferred Leader 選舉,默認時間間隔爲 300 秒,期間須要觀察 Preferred Leader 選舉情況。對於新增的 Broker,Kafka 是不會自動地分配已有主題的負載,即不會將主題的分區分配到新增的 Broker,但咱們能夠經過 Kafka 提供的 API 對主題分區進行重分配操做,具體操做以下:
echo '{"version":1,"topics":[{"topic":"sjzn_spark_binlog_order_topic"}]}' > sjzn_spark_binlog_order_topic.json
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個副本,生成的分配數據仍是挺大的,這裏就不一一貼出來了
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
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
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。
更多精彩文章請關注做者維護的公衆號「後端進階」,這是一個專一後端相關技術的公衆號。
關注公衆號並回復「後端」免費領取後端相關電子書籍。
歡迎分享,轉載請保留出處。