在一個(gè)月黑風(fēng)高的夜晚,突然收到現(xiàn)網(wǎng)生產(chǎn)環(huán)境 Kafka 消息積壓的告警,夢(mèng)中驚醒啊,馬上起來排查日志。
成都創(chuàng)新互聯(lián)長(zhǎng)期為上1000+客戶提供的網(wǎng)站建設(shè)服務(wù),團(tuán)隊(duì)從業(yè)經(jīng)驗(yàn)10年,關(guān)注不同地域、不同群體,并針對(duì)不同對(duì)象提供差異化的產(chǎn)品和服務(wù);打造開放共贏平臺(tái),與合作伙伴共同營(yíng)造健康的互聯(lián)網(wǎng)生態(tài)環(huán)境。為原州企業(yè)提供專業(yè)的網(wǎng)站制作、網(wǎng)站設(shè)計(jì),原州網(wǎng)站改版等技術(shù)服務(wù)。擁有十年豐富建站經(jīng)驗(yàn)和眾多成功案例,為您定制開發(fā)。
Coordinator 為何物? Coordinator 用于管理 Consumer Group 中各個(gè)成員,負(fù)責(zé)消費(fèi) offset 位移管理和 Consumer Rebalance 。 Consumer 在消費(fèi)時(shí)必須先確認(rèn) Consumer Group 對(duì)應(yīng)的 Coordinator ,隨后才能 join Group ,獲取對(duì)應(yīng)的 topic partition 進(jìn)行消費(fèi)。
那如何確定 Consumer Group 的 Coordinator 呢?分兩步走:
1 、一個(gè) Consumer Group 對(duì)應(yīng)一個(gè) __consumers_offsets 的分區(qū),首先先計(jì)算 Consumer Group 對(duì)應(yīng)的 __consumers_offsets 的分區(qū),計(jì)算公式如下:
__consumers_offsets partition# = Math.abs(groupId.hashCode() % groupMetadataTopicPartitionCount ,其中 groupMetadataTopicPartitionCount 由 offsets.topic.num.partitions 指定。
2 、 1 中計(jì)算的該 partition 的 leader 所在的 broker 就是被選定的 Coordinator 。
Coordinator 節(jié)點(diǎn)找到了,現(xiàn)在看看 Coordinator 是否有問題:
不出所料, Coordinator 對(duì)應(yīng)分區(qū) Leader 為 -1 ,消費(fèi)端程序會(huì)一直等待,直到 Leader 選出來為止,這就直接導(dǎo)致了消費(fèi)卡死。
為啥 Leader 無法選舉? Leader 選舉是由 Controller 負(fù)責(zé)的。 Controller 節(jié)點(diǎn)負(fù)責(zé)管理整個(gè)集群中分區(qū)和副本的狀態(tài),比如 partition 的 Leader 選舉, topic 創(chuàng)建,副本分配, partition 和 replica 擴(kuò)容等。現(xiàn)在我們看看 Controller 的日志:
1. 6 月 10 日 15:48:30,006 秒 Broker 1 成為 controller
此時(shí)感知的節(jié)點(diǎn)為 1 和 2 ,節(jié)點(diǎn) 3 在 zk 讀不出來:
31 秒 847 的時(shí)候把 __consumer_offsets 的分區(qū) 3 的 Leader 選為 1 , ISR 為 [1,2] , leader_epoch 為 14 :
再過 1 秒后才感知到 Controller 發(fā)生變化,自身清退
2. Broker 2 在其后幾百毫秒后 (15:48:30,936) 也成為 Controller
但是 Broker2 是感知到 Broker 3 節(jié)點(diǎn)是活的,日志如下 :
注意這個(gè)時(shí)間點(diǎn), Broker1 還沒在 zk 把 __consumer_offsets 的分區(qū) 3 的 Leader 從節(jié)點(diǎn) 3 改為 1 ,這樣 Broker 2 還認(rèn)為 Broker 3 是 Leader ,并且 Broker 3 在它認(rèn)為是活的,所以不需要重新選舉 Leader 。這樣一直保持了相當(dāng)長(zhǎng)的時(shí)間,即使 Broker 1 已經(jīng)把這個(gè)分區(qū)的 Leader 切換了,它也不感知。
3. Broker 2 在 12 號(hào)的 21:43:19 又感知 Broker 1 網(wǎng)絡(luò)中斷,并處理節(jié)點(diǎn)失敗事件:
因?yàn)? Broker 2 內(nèi)存中認(rèn)為 __consumer_offsets 分區(qū) 3 的 Leader 是 broker 3 ,所以不會(huì)觸發(fā)分區(qū) 3 的 Leader 切換。
Broker 2 但是在處理失敗的節(jié)點(diǎn) Broker 1 時(shí),會(huì)把副本從 ISR 列表中去掉,去掉前會(huì)讀一次 zk ,代碼如下:
但是發(fā)現(xiàn) zk 中分區(qū) 3 的 Leader 已經(jīng)變?yōu)? 1 , ISR 列表為 [1,2] ,當(dāng)要去掉的節(jié)點(diǎn) 1 就是 Leader 的時(shí)候, Leader 就會(huì)變?yōu)? -1 , ISR 只有 [2] ,從日志也可以看到:
這樣分區(qū) 3 的 Leader 一直為 -1 ,直到有新的事件觸發(fā)節(jié)點(diǎn) 2 重新選舉才能恢復(fù)(例如重啟某個(gè)節(jié)點(diǎn))。
出現(xiàn)網(wǎng)絡(luò)異常后,由于新老 controller 之間感知的可用節(jié)點(diǎn)不同,導(dǎo)致新 controller 對(duì)某個(gè)分區(qū)的 Leader 在內(nèi)存中的信息與 zk 記錄元數(shù)據(jù)的信息不一致,導(dǎo)致 controller 選舉流程出現(xiàn)錯(cuò)誤,選不出 Leader 。 需要有新的選舉事件才能觸發(fā) Leader 選出來,例如重啟。
這是一個(gè)典型的由于網(wǎng)絡(luò)異常導(dǎo)致腦裂,進(jìn)而出現(xiàn)了多個(gè) Controller ,菊廠分布式消息服務(wù) Kafka( https://www.huaweicloud.com/product/dmskafka.html ) 經(jīng)過電信級(jí)的可靠性驗(yàn)證,已經(jīng)完美解決了這些問題 。